Project

General

Profile

Download (106 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.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.CorruptIndexException;
24
import org.apache.lucene.queryParser.ParseException;
25
import org.apache.lucene.search.BooleanClause.Occur;
26
import org.apache.lucene.search.BooleanQuery;
27
import org.apache.lucene.search.Query;
28
import org.apache.lucene.search.SortField;
29
import org.springframework.beans.factory.annotation.Autowired;
30
import org.springframework.stereotype.Service;
31
import org.springframework.transaction.annotation.Transactional;
32

    
33
import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator;
34
import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
35
import eu.etaxonomy.cdm.api.service.config.NameDeletionConfigurator;
36
import eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator;
37
import eu.etaxonomy.cdm.api.service.exception.DataChangeNoRollbackException;
38
import eu.etaxonomy.cdm.api.service.exception.HomotypicalGroupChangeException;
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.DefaultPagerImpl;
42
import eu.etaxonomy.cdm.api.service.search.ISearchResultBuilder;
43
import eu.etaxonomy.cdm.api.service.search.LuceneMultiSearch;
44
import eu.etaxonomy.cdm.api.service.search.LuceneSearch;
45
import eu.etaxonomy.cdm.api.service.search.LuceneSearch.TopGroupsWithMaxScore;
46
import eu.etaxonomy.cdm.api.service.search.QueryFactory;
47
import eu.etaxonomy.cdm.api.service.search.SearchResult;
48
import eu.etaxonomy.cdm.api.service.search.SearchResultBuilder;
49
import eu.etaxonomy.cdm.api.service.util.TaxonRelationshipEdge;
50
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
51
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
52
import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge;
53
import eu.etaxonomy.cdm.hibernate.search.GroupByTaxonClassBridge;
54
import eu.etaxonomy.cdm.hibernate.search.MultilanguageTextFieldBridge;
55
import eu.etaxonomy.cdm.model.CdmBaseType;
56
import eu.etaxonomy.cdm.model.common.CdmBase;
57
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
58
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
59
import eu.etaxonomy.cdm.model.common.Language;
60
import eu.etaxonomy.cdm.model.common.OrderedTermVocabulary;
61
import eu.etaxonomy.cdm.model.common.RelationshipBase;
62
import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
63
import eu.etaxonomy.cdm.model.common.UuidAndTitleCache;
64
import eu.etaxonomy.cdm.model.description.DescriptionBase;
65
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
66
import eu.etaxonomy.cdm.model.description.Feature;
67
import eu.etaxonomy.cdm.model.description.IIdentificationKey;
68
import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
69
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
70
import eu.etaxonomy.cdm.model.description.TaxonDescription;
71
import eu.etaxonomy.cdm.model.description.TaxonInteraction;
72
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
73
import eu.etaxonomy.cdm.model.media.Media;
74
import eu.etaxonomy.cdm.model.media.MediaRepresentation;
75
import eu.etaxonomy.cdm.model.media.MediaUtils;
76
import eu.etaxonomy.cdm.model.molecular.DnaSample;
77
import eu.etaxonomy.cdm.model.molecular.Sequence;
78
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
79
import eu.etaxonomy.cdm.model.name.Rank;
80
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
81
import eu.etaxonomy.cdm.model.name.ZoologicalName;
82
import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
83
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
84
import eu.etaxonomy.cdm.model.reference.Reference;
85
import eu.etaxonomy.cdm.model.taxon.Classification;
86
import eu.etaxonomy.cdm.model.taxon.Synonym;
87
import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
88
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
89
import eu.etaxonomy.cdm.model.taxon.Taxon;
90
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
91
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
92
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
93
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
94
import eu.etaxonomy.cdm.persistence.dao.AbstractBeanInitializer;
95
import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;
96
import eu.etaxonomy.cdm.persistence.dao.common.IOrderedTermVocabularyDao;
97
import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
98
import eu.etaxonomy.cdm.persistence.dao.occurrence.IOccurrenceDao;
99
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
100
import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;
101
import eu.etaxonomy.cdm.persistence.query.MatchMode;
102
import eu.etaxonomy.cdm.persistence.query.OrderHint;
103
import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;
104
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
105

    
106

    
107
/**
108
 * @author a.kohlbecker
109
 * @date 10.09.2010
110
 *
111
 */
112
@Service
113
@Transactional(readOnly = true)
114
public class TaxonServiceImpl extends IdentifiableServiceBase<TaxonBase,ITaxonDao> implements ITaxonService{
115
    private static final Logger logger = Logger.getLogger(TaxonServiceImpl.class);
116

    
117
    public static final String POTENTIAL_COMBINATION_NAMESPACE = "Potential combination";
118

    
119
    public static final String INFERRED_EPITHET_NAMESPACE = "Inferred epithet";
120

    
121
    public static final String INFERRED_GENUS_NAMESPACE = "Inferred genus";
122

    
123

    
124
    @Autowired
125
    private ITaxonNameDao nameDao;
126

    
127
    @Autowired
128
    private INameService nameService;
129

    
130
    @Autowired
131
    private ICdmGenericDao genericDao;
132

    
133
    @Autowired
134
    private IDescriptionService descriptionService;
135

    
136
    @Autowired
137
    private IOrderedTermVocabularyDao orderedVocabularyDao;
138

    
139
    @Autowired
140
    private IOccurrenceDao occurrenceDao;
141

    
142
    @Autowired
143
    private AbstractBeanInitializer beanInitializer;
144

    
145
    /**
146
     * Constructor
147
     */
148
    public TaxonServiceImpl(){
149
        if (logger.isDebugEnabled()) { logger.debug("Load TaxonService Bean"); }
150
    }
151

    
152
    /**
153
     * FIXME Candidate for harmonization
154
     * rename searchByName ?
155
     */
156
    @Override
157
    public List<TaxonBase> searchTaxaByName(String name, Reference sec) {
158
        return dao.getTaxaByName(name, sec);
159
    }
160

    
161
    /**
162
     * FIXME Candidate for harmonization
163
     * list(Synonym.class, ...)
164
     *  (non-Javadoc)
165
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllSynonyms(int, int)
166
     */
167
    @Override
168
    public List<Synonym> getAllSynonyms(int limit, int start) {
169
        return dao.getAllSynonyms(limit, start);
170
    }
171

    
172
    /**
173
     * FIXME Candidate for harmonization
174
     * list(Taxon.class, ...)
175
     *  (non-Javadoc)
176
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllTaxa(int, int)
177
     */
178
    @Override
179
    public List<Taxon> getAllTaxa(int limit, int start) {
180
        return dao.getAllTaxa(limit, start);
181
    }
182

    
183
    /**
184
     * FIXME Candidate for harmonization
185
     * merge with getRootTaxa(Reference sec, ..., ...)
186
     *  (non-Javadoc)
187
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.reference.Reference, boolean)
188
     */
189
    @Override
190
    public List<Taxon> getRootTaxa(Reference sec, CdmFetch cdmFetch, boolean onlyWithChildren) {
191
        if (cdmFetch == null){
192
            cdmFetch = CdmFetch.NO_FETCH();
193
        }
194
        return dao.getRootTaxa(sec, cdmFetch, onlyWithChildren, false);
195
    }
196

    
197

    
198
    /* (non-Javadoc)
199
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getRootTaxa(eu.etaxonomy.cdm.model.name.Rank, eu.etaxonomy.cdm.model.reference.Reference, boolean, boolean)
200
     */
201
    @Override
202
    public List<Taxon> getRootTaxa(Rank rank, Reference sec, boolean onlyWithChildren,boolean withMisapplications, List<String> propertyPaths) {
203
        return dao.getRootTaxa(rank, sec, null, onlyWithChildren, withMisapplications, propertyPaths);
204
    }
205

    
206
    /* (non-Javadoc)
207
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllRelationships(int, int)
208
     */
209
    @Override
210
    public List<RelationshipBase> getAllRelationships(int limit, int start){
211
        return dao.getAllRelationships(limit, start);
212
    }
213

    
214
    /**
215
     * FIXME Candidate for harmonization
216
     * is this the same as termService.getVocabulary(VocabularyEnum.TaxonRelationshipType) ?
217
     */
218
    @Override
219
    @Deprecated
220
    public OrderedTermVocabulary<TaxonRelationshipType> getTaxonRelationshipTypeVocabulary() {
221

    
222
        String taxonRelTypeVocabularyId = "15db0cf7-7afc-4a86-a7d4-221c73b0c9ac";
223
        UUID uuid = UUID.fromString(taxonRelTypeVocabularyId);
224
        OrderedTermVocabulary<TaxonRelationshipType> taxonRelTypeVocabulary =
225
            (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
226
        return taxonRelTypeVocabulary;
227
    }
228

    
229

    
230

    
231
    /*
232
     * (non-Javadoc)
233
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#swapSynonymWithAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym)
234
     */
235
    @Override
236
    @Transactional(readOnly = false)
237
    public void swapSynonymAndAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon){
238

    
239
        TaxonNameBase<?,?> synonymName = synonym.getName();
240
        synonymName.removeTaxonBase(synonym);
241
        TaxonNameBase<?,?> taxonName = acceptedTaxon.getName();
242
        taxonName.removeTaxonBase(acceptedTaxon);
243

    
244
        synonym.setName(taxonName);
245
        acceptedTaxon.setName(synonymName);
246

    
247
        // the accepted taxon needs a new uuid because the concept has changed
248
        // FIXME this leads to an error "HibernateException: immutable natural identifier of an instance of eu.etaxonomy.cdm.model.taxon.Taxon was altered"
249
        //acceptedTaxon.setUuid(UUID.randomUUID());
250
    }
251

    
252

    
253
    /* (non-Javadoc)
254
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeSynonymToAcceptedTaxon(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
255
     */
256
    //TODO correct delete handling still needs to be implemented / checked
257
    @Override
258
    @Transactional(readOnly = false)
259
    public Taxon changeSynonymToAcceptedTaxon(Synonym synonym, Taxon acceptedTaxon, boolean deleteSynonym, boolean copyCitationInfo, Reference citation, String microCitation) throws HomotypicalGroupChangeException{
260

    
261
        TaxonNameBase<?,?> acceptedName = acceptedTaxon.getName();
262
        TaxonNameBase<?,?> synonymName = synonym.getName();
263
        HomotypicalGroup synonymHomotypicGroup = synonymName.getHomotypicalGroup();
264

    
265
        //check synonym is not homotypic
266
        if (acceptedName.getHomotypicalGroup().equals(synonymHomotypicGroup)){
267
            String message = "The accepted taxon and the synonym are part of the same homotypical group and therefore can not be both accepted.";
268
            throw new HomotypicalGroupChangeException(message);
269
        }
270

    
271
        Taxon newAcceptedTaxon = Taxon.NewInstance(synonymName, acceptedTaxon.getSec());
272

    
273
        SynonymRelationshipType relTypeForGroup = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
274
        List<Synonym> heteroSynonyms = acceptedTaxon.getSynonymsInGroup(synonymHomotypicGroup);
275

    
276
        for (Synonym heteroSynonym : heteroSynonyms){
277
            if (synonym.equals(heteroSynonym)){
278
                acceptedTaxon.removeSynonym(heteroSynonym, false);
279
            }else{
280
                //move synonyms in same homotypic group to new accepted taxon
281
                heteroSynonym.replaceAcceptedTaxon(newAcceptedTaxon, relTypeForGroup, copyCitationInfo, citation, microCitation);
282
            }
283
        }
284

    
285
        //synonym.getName().removeTaxonBase(synonym);
286
        //TODO correct delete handling still needs to be implemented / checked
287
        if (deleteSynonym){
288
//			deleteSynonym(synonym, taxon, false);
289
            try {
290
                this.dao.flush();
291
                this.delete(synonym);
292

    
293
            } catch (Exception e) {
294
                logger.info("Can't delete old synonym from database");
295
            }
296
        }
297

    
298
        return newAcceptedTaxon;
299
    }
300

    
301

    
302
    @Override
303
    public Taxon changeSynonymToRelatedTaxon(Synonym synonym, Taxon toTaxon, TaxonRelationshipType taxonRelationshipType, Reference citation, String microcitation){
304

    
305
        // Get name from synonym
306
        TaxonNameBase<?, ?> synonymName = synonym.getName();
307

    
308
        // remove synonym from taxon
309
        toTaxon.removeSynonym(synonym);
310

    
311
        // Create a taxon with synonym name
312
        Taxon fromTaxon = Taxon.NewInstance(synonymName, null);
313

    
314
        // Add taxon relation
315
        fromTaxon.addTaxonRelation(toTaxon, taxonRelationshipType, citation, microcitation);
316

    
317
        // since we are swapping names, we have to detach the name from the synonym completely.
318
        // Otherwise the synonym will still be in the list of typified names.
319
        synonym.getName().removeTaxonBase(synonym);
320

    
321
        return fromTaxon;
322
    }
323

    
324

    
325
    /* (non-Javadoc)
326
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#changeHomotypicalGroupOfSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.name.HomotypicalGroup, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
327
     */
328
    @Transactional(readOnly = false)
329
    @Override
330
    public void changeHomotypicalGroupOfSynonym(Synonym synonym, HomotypicalGroup newHomotypicalGroup, Taxon targetTaxon,
331
                        boolean removeFromOtherTaxa, boolean setBasionymRelationIfApplicable){
332
        // Get synonym name
333
        TaxonNameBase synonymName = synonym.getName();
334
        HomotypicalGroup oldHomotypicalGroup = synonymName.getHomotypicalGroup();
335

    
336

    
337
        // Switch groups
338
        oldHomotypicalGroup.removeTypifiedName(synonymName);
339
        newHomotypicalGroup.addTypifiedName(synonymName);
340

    
341
        //remove existing basionym relationships
342
        synonymName.removeBasionyms();
343

    
344
        //add basionym relationship
345
        if (setBasionymRelationIfApplicable){
346
            Set<TaxonNameBase> basionyms = newHomotypicalGroup.getBasionyms();
347
            for (TaxonNameBase basionym : basionyms){
348
                synonymName.addBasionym(basionym);
349
            }
350
        }
351

    
352
        //set synonym relationship correctly
353
//			SynonymRelationship relToTaxon = null;
354
        boolean relToTargetTaxonExists = false;
355
        Set<SynonymRelationship> existingRelations = synonym.getSynonymRelations();
356
        for (SynonymRelationship rel : existingRelations){
357
            Taxon acceptedTaxon = rel.getAcceptedTaxon();
358
            boolean isTargetTaxon = acceptedTaxon != null && acceptedTaxon.equals(targetTaxon);
359
            HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
360
            boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
361
            SynonymRelationshipType newRelationType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
362
            rel.setType(newRelationType);
363
            //TODO handle citation and microCitation
364

    
365
            if (isTargetTaxon){
366
                relToTargetTaxonExists = true;
367
            }else{
368
                if (removeFromOtherTaxa){
369
                    acceptedTaxon.removeSynonym(synonym, false);
370
                }else{
371
                    //do nothing
372
                }
373
            }
374
        }
375
        if (targetTaxon != null &&  ! relToTargetTaxonExists ){
376
            Taxon acceptedTaxon = targetTaxon;
377
            HomotypicalGroup acceptedGroup = acceptedTaxon.getHomotypicGroup();
378
            boolean isHomotypicToTaxon = acceptedGroup.equals(newHomotypicalGroup);
379
            SynonymRelationshipType relType = isHomotypicToTaxon? SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF() : SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
380
            //TODO handle citation and microCitation
381
            Reference citation = null;
382
            String microCitation = null;
383
            acceptedTaxon.addSynonym(synonym, relType, citation, microCitation);
384
        }
385

    
386
    }
387

    
388

    
389
    /* (non-Javadoc)
390
     * @see eu.etaxonomy.cdm.api.service.IIdentifiableEntityService#updateTitleCache(java.lang.Integer, eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy)
391
     */
392
    @Override
393
    @Transactional(readOnly = false)
394
    public void updateTitleCache(Class<? extends TaxonBase> clazz, Integer stepSize, IIdentifiableEntityCacheStrategy<TaxonBase> cacheStrategy, IProgressMonitor monitor) {
395
        if (clazz == null){
396
            clazz = TaxonBase.class;
397
        }
398
        super.updateTitleCacheImpl(clazz, stepSize, cacheStrategy, monitor);
399
    }
400

    
401
    @Override
402
    @Autowired
403
    protected void setDao(ITaxonDao dao) {
404
        this.dao = dao;
405
    }
406

    
407
    /* (non-Javadoc)
408
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)
409
     */
410
    @Override
411
    public Pager<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String uninomial,	String infragenericEpithet, String specificEpithet,	String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
412
        Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
413

    
414
        List<TaxonBase> results = new ArrayList<TaxonBase>();
415
        if(numberOfResults > 0) { // no point checking again
416
            results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
417
        }
418

    
419
        return new DefaultPagerImpl<TaxonBase>(pageNumber, numberOfResults, pageSize, results);
420
    }
421

    
422
    /* (non-Javadoc)
423
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxaByName(java.lang.Class, java.lang.String, java.lang.String, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.name.Rank, java.lang.Integer, java.lang.Integer)
424
     */
425
    @Override
426
    public List<TaxonBase> listTaxaByName(Class<? extends TaxonBase> clazz, String uninomial,	String infragenericEpithet, String specificEpithet,	String infraspecificEpithet, Rank rank, Integer pageSize,Integer pageNumber) {
427
        Integer numberOfResults = dao.countTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank);
428

    
429
        List<TaxonBase> results = new ArrayList<TaxonBase>();
430
        if(numberOfResults > 0) { // no point checking again
431
            results = dao.findTaxaByName(clazz, uninomial, infragenericEpithet, specificEpithet, infraspecificEpithet, rank, pageSize, pageNumber);
432
        }
433

    
434
        return results;
435
    }
436

    
437
    /* (non-Javadoc)
438
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
439
     */
440
    @Override
441
    public List<TaxonRelationship> listToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
442
        Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
443

    
444
        List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
445
        if(numberOfResults > 0) { // no point checking again
446
            results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
447
        }
448
        return results;
449
    }
450

    
451
    /* (non-Javadoc)
452
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#pageToTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
453
     */
454
    @Override
455
    public Pager<TaxonRelationship> pageToTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
456
        Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedTo);
457

    
458
        List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
459
        if(numberOfResults > 0) { // no point checking again
460
            results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedTo);
461
        }
462
        return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
463
    }
464

    
465
    /* (non-Javadoc)
466
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
467
     */
468
    @Override
469
    public List<TaxonRelationship> listFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){
470
        Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
471

    
472
        List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
473
        if(numberOfResults > 0) { // no point checking again
474
            results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
475
        }
476
        return results;
477
    }
478

    
479
    /* (non-Javadoc)
480
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#pageFromTaxonRelationships(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
481
     */
482
    @Override
483
    public Pager<TaxonRelationship> pageFromTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
484
        Integer numberOfResults = dao.countTaxonRelationships(taxon, type, TaxonRelationship.Direction.relatedFrom);
485

    
486
        List<TaxonRelationship> results = new ArrayList<TaxonRelationship>();
487
        if(numberOfResults > 0) { // no point checking again
488
            results = dao.getTaxonRelationships(taxon, type, pageSize, pageNumber, orderHints, propertyPaths, TaxonRelationship.Direction.relatedFrom);
489
        }
490
        return new DefaultPagerImpl<TaxonRelationship>(pageNumber, numberOfResults, pageSize, results);
491
    }
492

    
493
    /**
494
     * @param taxon
495
     * @param includeRelationships
496
     * @param maxDepth
497
     * @param limit
498
     * @param starts
499
     * @param propertyPaths
500
     * @return an List which is not specifically ordered
501
     */
502
    @Override
503
    public Set<Taxon> listRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Integer maxDepth,
504
            Integer limit, Integer start, List<String> propertyPaths) {
505

    
506
        Set<Taxon> relatedTaxa = collectRelatedTaxa(taxon, includeRelationships, new HashSet<Taxon>(), maxDepth);
507
        relatedTaxa.remove(taxon);
508
        beanInitializer.initializeAll(relatedTaxa, propertyPaths);
509
        return relatedTaxa;
510
    }
511

    
512

    
513
    /**
514
     * recursively collect related taxa for the given <code>taxon</code> . The returned list will also include the
515
     *  <code>taxon</code> supplied as parameter.
516
     *
517
     * @param taxon
518
     * @param includeRelationships
519
     * @param taxa
520
     * @param maxDepth can be <code>null</code> for infinite depth
521
     * @return
522
     */
523
    private Set<Taxon> collectRelatedTaxa(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, Set<Taxon> taxa, Integer maxDepth) {
524

    
525
        if(taxa.isEmpty()) {
526
            taxa.add(taxon);
527
        }
528

    
529
        if(maxDepth != null) {
530
            maxDepth--;
531
        }
532
        if(logger.isDebugEnabled()){
533
            logger.debug("collecting related taxa for " + taxon + " with maxDepth=" + maxDepth);
534
        }
535
        List<TaxonRelationship> taxonRelationships = dao.getTaxonRelationships(taxon, null, null, null, null, null, null);
536
        for (TaxonRelationship taxRel : taxonRelationships) {
537

    
538
            // skip invalid data
539
            if (taxRel.getToTaxon() == null || taxRel.getFromTaxon() == null || taxRel.getType() == null) {
540
                continue;
541
            }
542
            // filter by includeRelationships
543
            for (TaxonRelationshipEdge relationshipEdgeFilter : includeRelationships) {
544
                if ( relationshipEdgeFilter.getTaxonRelationshipType().equals(taxRel.getType()) ) {
545
                    if (relationshipEdgeFilter.getDirections().contains(Direction.relatedTo) && !taxa.contains(taxRel.getToTaxon())) {
546
                        if(logger.isDebugEnabled()){
547
                            logger.debug(maxDepth + ": " + taxon.getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxRel.getToTaxon().getTitleCache());
548
                        }
549
                        taxa.add(taxRel.getToTaxon());
550
                        if(maxDepth == null || maxDepth > 0) {
551
                            taxa.addAll(collectRelatedTaxa(taxRel.getToTaxon(), includeRelationships, taxa, maxDepth));
552
                        }
553
                    }
554
                    if(relationshipEdgeFilter.getDirections().contains(Direction.relatedFrom) && !taxa.contains(taxRel.getFromTaxon())) {
555
                        taxa.add(taxRel.getFromTaxon());
556
                        if(logger.isDebugEnabled()){
557
                            logger.debug(maxDepth + ": " +taxRel.getFromTaxon().getTitleCache() + " --[" + taxRel.getType().getLabel() + "]--> " + taxon.getTitleCache() );
558
                        }
559
                        if(maxDepth == null || maxDepth > 0) {
560
                            taxa.addAll(collectRelatedTaxa(taxRel.getFromTaxon(), includeRelationships, taxa, maxDepth));
561
                        }
562
                    }
563
                }
564
            }
565
        }
566
        return taxa;
567
    }
568

    
569
    /* (non-Javadoc)
570
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getSynonyms(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
571
     */
572
    @Override
573
    public Pager<SynonymRelationship> getSynonyms(Taxon taxon,	SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
574
        Integer numberOfResults = dao.countSynonyms(taxon, type);
575

    
576
        List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
577
        if(numberOfResults > 0) { // no point checking again
578
            results = dao.getSynonyms(taxon, type, pageSize, pageNumber, orderHints, propertyPaths);
579
        }
580

    
581
        return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
582
    }
583

    
584
    /* (non-Javadoc)
585
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getSynonyms(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
586
     */
587
    @Override
588
    public Pager<SynonymRelationship> getSynonyms(Synonym synonym,	SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
589
        Integer numberOfResults = dao.countSynonyms(synonym, type);
590

    
591
        List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
592
        if(numberOfResults > 0) { // no point checking again
593
            results = dao.getSynonyms(synonym, type, pageSize, pageNumber, orderHints, propertyPaths);
594
        }
595

    
596
        return new DefaultPagerImpl<SynonymRelationship>(pageNumber, numberOfResults, pageSize, results);
597
    }
598

    
599
    /* (non-Javadoc)
600
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHomotypicSynonymsByHomotypicGroup(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
601
     */
602
    @Override
603
    public List<Synonym> getHomotypicSynonymsByHomotypicGroup(Taxon taxon, List<String> propertyPaths){
604
        Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
605
        return t.getHomotypicSynonymsByHomotypicGroup();
606
    }
607

    
608
    /* (non-Javadoc)
609
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getHeterotypicSynonymyGroups(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.List)
610
     */
611
    @Override
612
    public List<List<Synonym>> getHeterotypicSynonymyGroups(Taxon taxon, List<String> propertyPaths){
613
        Taxon t = (Taxon)dao.load(taxon.getUuid(), propertyPaths);
614
        List<HomotypicalGroup> homotypicalGroups = t.getHeterotypicSynonymyGroups();
615
        List<List<Synonym>> heterotypicSynonymyGroups = new ArrayList<List<Synonym>>(homotypicalGroups.size());
616
        for(HomotypicalGroup homotypicalGroup : homotypicalGroups){
617
            heterotypicSynonymyGroups.add(t.getSynonymsInGroup(homotypicalGroup));
618
        }
619
        return heterotypicSynonymyGroups;
620
    }
621

    
622
    @Override
623
    public List<UuidAndTitleCache<TaxonBase>> findTaxaAndNamesForEditor(IFindTaxaAndNamesConfigurator configurator){
624

    
625
        List<UuidAndTitleCache<TaxonBase>> result = new ArrayList<UuidAndTitleCache<TaxonBase>>();
626
//        Class<? extends TaxonBase> clazz = null;
627
//        if ((configurator.isDoTaxa() && configurator.isDoSynonyms())) {
628
//            clazz = TaxonBase.class;
629
//            //propertyPath.addAll(configurator.getTaxonPropertyPath());
630
//            //propertyPath.addAll(configurator.getSynonymPropertyPath());
631
//        } else if(configurator.isDoTaxa()) {
632
//            clazz = Taxon.class;
633
//            //propertyPath = configurator.getTaxonPropertyPath();
634
//        } else if (configurator.isDoSynonyms()) {
635
//            clazz = Synonym.class;
636
//            //propertyPath = configurator.getSynonymPropertyPath();
637
//        }
638

    
639

    
640
        result = dao.getTaxaByNameForEditor(configurator.isDoTaxa(), configurator.isDoSynonyms(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
641
        return result;
642
    }
643

    
644
    /* (non-Javadoc)
645
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaAndNames(eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator)
646
     */
647
    @Override
648
    public Pager<IdentifiableEntity> findTaxaAndNames(IFindTaxaAndNamesConfigurator configurator) {
649

    
650
        List<IdentifiableEntity> results = new ArrayList<IdentifiableEntity>();
651
        int numberOfResults = 0; // overall number of results (as opposed to number of results per page)
652
        List<TaxonBase> taxa = null;
653

    
654
        // Taxa and synonyms
655
        long numberTaxaResults = 0L;
656

    
657

    
658
        List<String> propertyPath = new ArrayList<String>();
659
        if(configurator.getTaxonPropertyPath() != null){
660
            propertyPath.addAll(configurator.getTaxonPropertyPath());
661
        }
662

    
663

    
664
       if (configurator.isDoMisappliedNames() || configurator.isDoSynonyms() || configurator.isDoTaxa()){
665
            if(configurator.getPageSize() != null){ // no point counting if we need all anyway
666
                numberTaxaResults =
667
                    dao.countTaxaByName(configurator.isDoTaxa(),configurator.isDoSynonyms(), configurator.isDoMisappliedNames(),
668
                        configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(),
669
                        configurator.getNamedAreas());
670
            }
671

    
672
            if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){ // no point checking again if less results
673
                taxa = dao.getTaxaByName(configurator.isDoTaxa(), configurator.isDoSynonyms(),
674
                    configurator.isDoMisappliedNames(), configurator.getTitleSearchStringSqlized(), configurator.getClassification(),
675
                    configurator.getMatchMode(), configurator.getNamedAreas(),
676
                    configurator.getPageSize(), configurator.getPageNumber(), propertyPath);
677
            }
678
       }
679

    
680
        if (logger.isDebugEnabled()) { logger.debug(numberTaxaResults + " matching taxa counted"); }
681

    
682
        if(taxa != null){
683
            results.addAll(taxa);
684
        }
685

    
686
        numberOfResults += numberTaxaResults;
687

    
688
        // Names without taxa
689
        if (configurator.isDoNamesWithoutTaxa()) {
690
            int numberNameResults = 0;
691

    
692
            List<? extends TaxonNameBase<?,?>> names =
693
                nameDao.findByName(configurator.getTitleSearchStringSqlized(), configurator.getMatchMode(),
694
                        configurator.getPageSize(), configurator.getPageNumber(), null, configurator.getTaxonNamePropertyPath());
695
            if (logger.isDebugEnabled()) { logger.debug(names.size() + " matching name(s) found"); }
696
            if (names.size() > 0) {
697
                for (TaxonNameBase<?,?> taxonName : names) {
698
                    if (taxonName.getTaxonBases().size() == 0) {
699
                        results.add(taxonName);
700
                        numberNameResults++;
701
                    }
702
                }
703
                if (logger.isDebugEnabled()) { logger.debug(numberNameResults + " matching name(s) without taxa found"); }
704
                numberOfResults += numberNameResults;
705
            }
706
        }
707

    
708
        // Taxa from common names
709

    
710
        if (configurator.isDoTaxaByCommonNames()) {
711
            taxa = new ArrayList<TaxonBase>();
712
            numberTaxaResults = 0;
713
            if(configurator.getPageSize() != null){// no point counting if we need all anyway
714
                numberTaxaResults = dao.countTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas());
715
            }
716
            if(configurator.getPageSize() == null || numberTaxaResults > configurator.getPageSize() * configurator.getPageNumber()){
717
                List<Object[]> commonNameResults = dao.getTaxaByCommonName(configurator.getTitleSearchStringSqlized(), configurator.getClassification(), configurator.getMatchMode(), configurator.getNamedAreas(), configurator.getPageSize(), configurator.getPageNumber(), configurator.getTaxonPropertyPath());
718
                for( Object[] entry : commonNameResults ) {
719
                    taxa.add((TaxonBase) entry[0]);
720
                }
721
            }
722
            if(taxa != null){
723
                results.addAll(taxa);
724
            }
725
            numberOfResults += numberTaxaResults;
726

    
727
        }
728

    
729
       return new DefaultPagerImpl<IdentifiableEntity>
730
            (configurator.getPageNumber(), numberOfResults, configurator.getPageSize(), results);
731
    }
732

    
733
    public List<UuidAndTitleCache<TaxonBase>> getTaxonUuidAndTitleCache(){
734
        return dao.getUuidAndTitleCache();
735
    }
736

    
737
    /* (non-Javadoc)
738
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getAllMedia(eu.etaxonomy.cdm.model.taxon.Taxon, int, int, int, java.lang.String[])
739
     */
740
    @Override
741
    public List<MediaRepresentation> getAllMedia(Taxon taxon, int size, int height, int widthOrDuration, String[] mimeTypes){
742
        List<MediaRepresentation> medRep = new ArrayList<MediaRepresentation>();
743
        taxon = (Taxon)dao.load(taxon.getUuid());
744
        Set<TaxonDescription> descriptions = taxon.getDescriptions();
745
        for (TaxonDescription taxDesc: descriptions){
746
            Set<DescriptionElementBase> elements = taxDesc.getElements();
747
            for (DescriptionElementBase descElem: elements){
748
                for(Media media : descElem.getMedia()){
749

    
750
                    //find the best matching representation
751
                    medRep.add(MediaUtils.findBestMatchingRepresentation(media, null, size, height, widthOrDuration, mimeTypes));
752

    
753
                }
754
            }
755
        }
756
        return medRep;
757
    }
758

    
759
    /* (non-Javadoc)
760
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listTaxonDescriptionMedia(eu.etaxonomy.cdm.model.taxon.Taxon, boolean)
761
     */
762
    @Override
763
    public List<Media> listTaxonDescriptionMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships, boolean limitToGalleries, List<String> propertyPath){
764
        return listMedia(taxon, includeRelationships, limitToGalleries, true, false, false, propertyPath);
765
    }
766

    
767

    
768
    /* (non-Javadoc)
769
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listMedia(eu.etaxonomy.cdm.model.taxon.Taxon, java.util.Set, boolean, java.util.List)
770
     */
771
    @Override
772
    public List<Media> listMedia(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships,
773
            Boolean limitToGalleries, Boolean includeTaxonDescriptions, Boolean includeOccurrences,
774
            Boolean includeTaxonNameDescriptions, List<String> propertyPath) {
775

    
776
        Set<Taxon> taxa = new HashSet<Taxon>();
777
        List<Media> taxonMedia = new ArrayList<Media>();
778

    
779
        if (limitToGalleries == null) {
780
            limitToGalleries = false;
781
        }
782

    
783
        // --- resolve related taxa
784
        if (includeRelationships != null) {
785
            taxa = listRelatedTaxa(taxon, includeRelationships, null, null, null, null);
786
        }
787

    
788
        taxa.add((Taxon) dao.load(taxon.getUuid()));
789

    
790
        if(includeTaxonDescriptions != null && includeTaxonDescriptions){
791
            List<TaxonDescription> taxonDescriptions = new ArrayList<TaxonDescription>();
792
            // --- TaxonDescriptions
793
            for (Taxon t : taxa) {
794
                taxonDescriptions.addAll(descriptionService.listTaxonDescriptions(t, null, null, null, null, propertyPath));
795
            }
796
            for (TaxonDescription taxonDescription : taxonDescriptions) {
797
                if (!limitToGalleries || taxonDescription.isImageGallery()) {
798
                    for (DescriptionElementBase element : taxonDescription.getElements()) {
799
                        for (Media media : element.getMedia()) {
800
                            taxonMedia.add(media);
801
                        }
802
                    }
803
                }
804
            }
805
        }
806

    
807
        if(includeOccurrences != null && includeOccurrences) {
808
            Set<SpecimenOrObservationBase> specimensOrObservations = new HashSet<SpecimenOrObservationBase>();
809
            // --- Specimens
810
            for (Taxon t : taxa) {
811
                specimensOrObservations.addAll(occurrenceDao.listByAssociatedTaxon(null, t, null, null, null, null));
812
            }
813
            for (SpecimenOrObservationBase occurrence : specimensOrObservations) {
814

    
815
                taxonMedia.addAll(occurrence.getMedia());
816

    
817
                // SpecimenDescriptions
818
                Set<SpecimenDescription> specimenDescriptions = occurrence.getSpecimenDescriptions();
819
                for (DescriptionBase specimenDescription : specimenDescriptions) {
820
                    if (!limitToGalleries || specimenDescription.isImageGallery()) {
821
                        Set<DescriptionElementBase> elements = specimenDescription.getElements();
822
                        for (DescriptionElementBase element : elements) {
823
                            for (Media media : element.getMedia()) {
824
                                taxonMedia.add(media);
825
                            }
826
                        }
827
                    }
828
                }
829

    
830
                // Collection
831
                if (occurrence instanceof DerivedUnitBase) {
832
                    if (((DerivedUnitBase) occurrence).getCollection() != null){
833
                        taxonMedia.addAll(((DerivedUnitBase) occurrence).getCollection().getMedia());
834
                    }
835
                }
836

    
837
                // Chromatograms
838
                if (occurrence instanceof DnaSample) {
839
                    Set<Sequence> sequences = ((DnaSample) occurrence).getSequences();
840
                    for (Sequence sequence : sequences) {
841
                        taxonMedia.addAll(sequence.getChromatograms());
842
                    }
843
                }
844

    
845
            }
846
        }
847

    
848
        if(includeTaxonNameDescriptions != null && includeTaxonNameDescriptions) {
849
            // --- TaxonNameDescription
850
            Set<TaxonNameDescription> nameDescriptions = new HashSet<TaxonNameDescription>();
851
            for (Taxon t : taxa) {
852
                nameDescriptions .addAll(t.getName().getDescriptions());
853
            }
854
            for(TaxonNameDescription nameDescription: nameDescriptions){
855
                if (!limitToGalleries || nameDescription.isImageGallery()) {
856
                    Set<DescriptionElementBase> elements = nameDescription.getElements();
857
                    for (DescriptionElementBase element : elements) {
858
                        for (Media media : element.getMedia()) {
859
                            taxonMedia.add(media);
860
                        }
861
                    }
862
                }
863
            }
864
        }
865

    
866
        beanInitializer.initializeAll(taxonMedia, propertyPath);
867
        return taxonMedia;
868
    }
869

    
870
    /* (non-Javadoc)
871
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxaByID(java.util.Set)
872
     */
873
    @Override
874
    public List<TaxonBase> findTaxaByID(Set<Integer> listOfIDs) {
875
        return this.dao.listByIds(listOfIDs, null, null, null, null);
876
    }
877

    
878
    /* (non-Javadoc)
879
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findTaxonByUuid(UUID uuid, List<String> propertyPaths)
880
     */
881
    @Override
882
    public TaxonBase findTaxonByUuid(UUID uuid, List<String> propertyPaths){
883
        return this.dao.findByUuid(uuid, null ,propertyPaths);
884
    }
885

    
886
    /* (non-Javadoc)
887
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#countAllRelationships()
888
     */
889
    @Override
890
    public int countAllRelationships() {
891
        return this.dao.countAllRelationships();
892
    }
893

    
894

    
895

    
896

    
897
    /* (non-Javadoc)
898
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNames(java.util.List)
899
     */
900
    @Override
901
    public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPath) {
902
        return this.dao.findIdenticalTaxonNames(propertyPath);
903
    }
904

    
905

    
906
    /* (non-Javadoc)
907
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteTaxon(eu.etaxonomy.cdm.model.taxon.Taxon, eu.etaxonomy.cdm.api.service.config.TaxonDeletionConfigurator)
908
     */
909
    @Override
910
    public void deleteTaxon(Taxon taxon, TaxonDeletionConfigurator config) throws ReferencedObjectUndeletableException {
911
        if (config == null){
912
            config = new TaxonDeletionConfigurator();
913
        }
914

    
915
            //    	TaxonNode
916
            if (! config.isDeleteTaxonNodes()){
917
                if (taxon.getTaxonNodes().size() > 0){
918
                    String message = "Taxon can't be deleted as it is used in a classification node. Remove taxon from all classifications prior to deletion.";
919
                    throw new ReferencedObjectUndeletableException(message);
920
                }
921
            }
922

    
923

    
924
            //    	SynonymRelationShip
925
            if (config.isDeleteSynonymRelations()){
926
                boolean removeSynonymNameFromHomotypicalGroup = false;
927
                for (SynonymRelationship synRel : taxon.getSynonymRelations()){
928
                    Synonym synonym = synRel.getSynonym();
929
                    taxon.removeSynonymRelation(synRel, removeSynonymNameFromHomotypicalGroup);
930
                    if (config.isDeleteSynonymsIfPossible()){
931
                        //TODO which value
932
                        boolean newHomotypicGroupIfNeeded = true;
933
                        deleteSynonym(synonym, taxon, config.isDeleteNameIfPossible(), newHomotypicGroupIfNeeded);
934
                    }else{
935
                        deleteSynonymRelationships(synonym, taxon);
936
                    }
937
                }
938
            }
939

    
940
            //    	TaxonRelationship
941
            if (! config.isDeleteTaxonRelationships()){
942
                if (taxon.getTaxonRelations().size() > 0){
943
                    String message = "Taxon can't be deleted as it is related to another taxon. Remove taxon from all relations to other taxa prior to deletion.";
944
                    throw new ReferencedObjectUndeletableException(message);
945
                }
946
            }
947

    
948

    
949
            //    	TaxonDescription
950
                    Set<TaxonDescription> descriptions = taxon.getDescriptions();
951

    
952
                    for (TaxonDescription desc: descriptions){
953
                        if (config.isDeleteDescriptions()){
954
                            //TODO use description delete configurator ?
955
                            //FIXME check if description is ALWAYS deletable
956
                            descriptionService.delete(desc);
957
                        }else{
958
                            if (desc.getDescribedSpecimenOrObservations().size()>0){
959
                                String message = "Taxon can't be deleted as it is used in a TaxonDescription" +
960
                                        " which also describes specimens or abservations";
961
                                    throw new ReferencedObjectUndeletableException(message);
962
                                }
963
                            }
964
                        }
965

    
966

    
967
                //check references with only reverse mapping
968
            Set<CdmBase> referencingObjects = genericDao.getReferencingObjects(taxon);
969
            for (CdmBase referencingObject : referencingObjects){
970
                //IIdentificationKeys (Media, Polytomous, MultiAccess)
971
                if (HibernateProxyHelper.isInstanceOf(referencingObject, IIdentificationKey.class)){
972
                    String message = "Taxon can't be deleted as it is used in an identification key. Remove from identification key prior to deleting this name";
973
                    message = String.format(message, CdmBase.deproxy(referencingObject, DerivedUnitBase.class).getTitleCache());
974
                    throw new ReferencedObjectUndeletableException(message);
975
                }
976

    
977

    
978
                //PolytomousKeyNode
979
                if (referencingObject.isInstanceOf(PolytomousKeyNode.class)){
980
                    String message = "Taxon can't be deleted as it is used in polytomous key node";
981
                    throw new ReferencedObjectUndeletableException(message);
982
                }
983

    
984
                //TaxonInteraction
985
                if (referencingObject.isInstanceOf(TaxonInteraction.class)){
986
                    String message = "Taxon can't be deleted as it is used in taxonInteraction#taxon2";
987
                    throw new ReferencedObjectUndeletableException(message);
988
                }
989
            }
990

    
991

    
992
            //TaxonNameBase
993
            if (config.isDeleteNameIfPossible()){
994
                try {
995
                    nameService.delete(taxon.getName(), config.getNameDeletionConfig());
996
                } catch (ReferencedObjectUndeletableException e) {
997
                    //do nothing
998
                    if (logger.isDebugEnabled()){logger.debug("Name could not be deleted");}
999
                }
1000
            }
1001

    
1002
    }
1003

    
1004
    /* (non-Javadoc)
1005
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonym(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, boolean)
1006
     */
1007
    @Transactional(readOnly = false)
1008
    @Override
1009
    public void deleteSynonym(Synonym synonym, Taxon taxon, boolean removeNameIfPossible,boolean newHomotypicGroupIfNeeded) {
1010
        if (synonym == null){
1011
            return;
1012
        }
1013
        synonym = CdmBase.deproxy(dao.merge(synonym), Synonym.class);
1014

    
1015
        //remove synonymRelationship
1016
        Set<Taxon> taxonSet = new HashSet<Taxon>();
1017
        if (taxon != null){
1018
            taxonSet.add(taxon);
1019
        }else{
1020
            taxonSet.addAll(synonym.getAcceptedTaxa());
1021
        }
1022
        for (Taxon relatedTaxon : taxonSet){
1023
//			dao.deleteSynonymRelationships(synonym, relatedTaxon);
1024
            relatedTaxon.removeSynonym(synonym, newHomotypicGroupIfNeeded);
1025
        }
1026
        this.saveOrUpdate(synonym);
1027

    
1028
        //TODO remove name from homotypical group?
1029

    
1030
        //remove synonym (if necessary)
1031
        if (synonym.getSynonymRelations().isEmpty()){
1032
            TaxonNameBase<?,?> name = synonym.getName();
1033
            synonym.setName(null);
1034
            dao.delete(synonym);
1035

    
1036
            //remove name if possible (and required)
1037
            if (name != null && removeNameIfPossible){
1038
                try{
1039
                    nameService.delete(name, new NameDeletionConfigurator());
1040
                }catch (DataChangeNoRollbackException ex){
1041
                    if (logger.isDebugEnabled()) {
1042
                        logger.debug("Name wasn't deleted as it is referenced");
1043
                    }
1044
                }
1045
            }
1046
        }
1047
    }
1048

    
1049

    
1050
    /* (non-Javadoc)
1051
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findIdenticalTaxonNameIds(java.util.List)
1052
     */
1053
    @Override
1054
    public List<TaxonNameBase> findIdenticalTaxonNameIds(List<String> propertyPath) {
1055

    
1056
        return this.dao.findIdenticalNamesNew(propertyPath);
1057
    }
1058

    
1059
    /* (non-Javadoc)
1060
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getPhylumName(eu.etaxonomy.cdm.model.name.TaxonNameBase)
1061
     */
1062
    @Override
1063
    public String getPhylumName(TaxonNameBase name){
1064
        return this.dao.getPhylumName(name);
1065
    }
1066

    
1067
    /* (non-Javadoc)
1068
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym, eu.etaxonomy.cdm.model.taxon.Taxon)
1069
     */
1070
    @Override
1071
    public long deleteSynonymRelationships(Synonym syn, Taxon taxon) {
1072
        return dao.deleteSynonymRelationships(syn, taxon);
1073
    }
1074

    
1075
/* (non-Javadoc)
1076
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#deleteSynonymRelationships(eu.etaxonomy.cdm.model.taxon.Synonym)
1077
     */
1078
    @Override
1079
    public long deleteSynonymRelationships(Synonym syn) {
1080
        return dao.deleteSynonymRelationships(syn, null);
1081
    }
1082

    
1083

    
1084
    /* (non-Javadoc)
1085
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listSynonymRelationships(eu.etaxonomy.cdm.model.taxon.TaxonBase, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List, eu.etaxonomy.cdm.model.common.RelationshipBase.Direction)
1086
     */
1087
    @Override
1088
    public List<SynonymRelationship> listSynonymRelationships(
1089
            TaxonBase taxonBase, SynonymRelationshipType type, Integer pageSize, Integer pageNumber,
1090
            List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {
1091
        Integer numberOfResults = dao.countSynonymRelationships(taxonBase, type, direction);
1092

    
1093
        List<SynonymRelationship> results = new ArrayList<SynonymRelationship>();
1094
        if(numberOfResults > 0) { // no point checking again
1095
            results = dao.getSynonymRelationships(taxonBase, type, pageSize, pageNumber, orderHints, propertyPaths, direction);
1096
        }
1097
        return results;
1098
    }
1099

    
1100
    /* (non-Javadoc)
1101
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingTaxon(java.lang.String)
1102
     */
1103
    @Override
1104
    public Taxon findBestMatchingTaxon(String taxonName) {
1105
        MatchingTaxonConfigurator config = MatchingTaxonConfigurator.NewInstance();
1106
        config.setTaxonNameTitle(taxonName);
1107
        return findBestMatchingTaxon(config);
1108
    }
1109

    
1110

    
1111

    
1112
    @Override
1113
    public Taxon findBestMatchingTaxon(MatchingTaxonConfigurator config) {
1114

    
1115
        Taxon bestCandidate = null;
1116
        try{
1117
            // 1. search for acceptet taxa
1118
            List<TaxonBase> taxonList = dao.findByNameTitleCache(true, false, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
1119
            boolean bestCandidateMatchesSecUuid = false;
1120
            boolean bestCandidateIsInClassification = false;
1121
            int countEqualCandidates = 0;
1122
            for(TaxonBase taxonBaseCandidate : taxonList){
1123
                if(taxonBaseCandidate instanceof Taxon){
1124
                    Taxon newCanditate = CdmBase.deproxy(taxonBaseCandidate, Taxon.class);
1125
                    boolean newCandidateMatchesSecUuid = isMatchesSecUuid(newCanditate, config);
1126
                    if (! newCandidateMatchesSecUuid && config.isOnlyMatchingSecUuid() ){
1127
                        continue;
1128
                    }else if(newCandidateMatchesSecUuid && ! bestCandidateMatchesSecUuid){
1129
                        bestCandidate = newCanditate;
1130
                        countEqualCandidates = 1;
1131
                        bestCandidateMatchesSecUuid = true;
1132
                        continue;
1133
                    }
1134

    
1135
                    boolean newCandidateInClassification = isInClassification(newCanditate, config);
1136
                    if (! newCandidateInClassification && config.isOnlyMatchingClassificationUuid()){
1137
                        continue;
1138
                    }else if (newCandidateInClassification && ! bestCandidateIsInClassification){
1139
                        bestCandidate = newCanditate;
1140
                        countEqualCandidates = 1;
1141
                        bestCandidateIsInClassification = true;
1142
                        continue;
1143
                    }
1144
                    if (bestCandidate == null){
1145
                        bestCandidate = newCanditate;
1146
                        countEqualCandidates = 1;
1147
                        continue;
1148
                    }
1149

    
1150
                }else{  //not Taxon.class
1151
                    continue;
1152
                }
1153
                countEqualCandidates++;
1154

    
1155
            }
1156
            if (bestCandidate != null){
1157
                if(countEqualCandidates > 1){
1158
                    logger.info(countEqualCandidates + " equally matching TaxonBases found, using first accepted Taxon: " + bestCandidate.getTitleCache());
1159
                    return bestCandidate;
1160
                } else {
1161
                    logger.info("using accepted Taxon: " + bestCandidate.getTitleCache());
1162
                    return bestCandidate;
1163
                }
1164
            }
1165

    
1166

    
1167
            // 2. search for synonyms
1168
            if (config.isIncludeSynonyms()){
1169
                List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, config.getTaxonNameTitle(), null, MatchMode.EXACT, null, 0, null, null);
1170
                for(TaxonBase taxonBase : synonymList){
1171
                    if(taxonBase instanceof Synonym){
1172
                        Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
1173
                        Set<Taxon> acceptetdCandidates = synonym.getAcceptedTaxa();
1174
                        if(!acceptetdCandidates.isEmpty()){
1175
                            bestCandidate = acceptetdCandidates.iterator().next();
1176
                            if(acceptetdCandidates.size() == 1){
1177
                                logger.info(acceptetdCandidates.size() + " Accepted taxa found for synonym " + taxonBase.getTitleCache() + ", using first one: " + bestCandidate.getTitleCache());
1178
                                return bestCandidate;
1179
                            } else {
1180
                                logger.info("using accepted Taxon " +  bestCandidate.getTitleCache() + "for synonym " + taxonBase.getTitleCache());
1181
                                return bestCandidate;
1182
                            }
1183
                            //TODO extend method: search using treeUUID, using SecUUID, first find accepted then include synonyms until a matching taxon is found
1184
                        }
1185
                    }
1186
                }
1187
            }
1188

    
1189
        } catch (Exception e){
1190
            logger.error(e);
1191
            e.printStackTrace();
1192
        }
1193

    
1194
        return bestCandidate;
1195
    }
1196

    
1197
    private boolean isInClassification(Taxon taxon, MatchingTaxonConfigurator config) {
1198
        UUID configClassificationUuid = config.getClassificationUuid();
1199
        if (configClassificationUuid == null){
1200
            return false;
1201
        }
1202
        for (TaxonNode node : taxon.getTaxonNodes()){
1203
            UUID classUuid = node.getClassification().getUuid();
1204
            if (configClassificationUuid.equals(classUuid)){
1205
                return true;
1206
            }
1207
        }
1208
        return false;
1209
    }
1210

    
1211
    private boolean isMatchesSecUuid(Taxon taxon, MatchingTaxonConfigurator config) {
1212
        UUID configSecUuid = config.getSecUuid();
1213
        if (configSecUuid == null){
1214
            return false;
1215
        }
1216
        UUID taxonSecUuid = (taxon.getSec() == null)? null : taxon.getSec().getUuid();
1217
        return configSecUuid.equals(taxonSecUuid);
1218
    }
1219

    
1220
    /* (non-Javadoc)
1221
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findBestMatchingSynonym(java.lang.String)
1222
     */
1223
    @Override
1224
    public Synonym findBestMatchingSynonym(String taxonName) {
1225
        List<TaxonBase> synonymList = dao.findByNameTitleCache(false, true, taxonName, null, MatchMode.EXACT, null, 0, null, null);
1226
        if(! synonymList.isEmpty()){
1227
            Synonym result = CdmBase.deproxy(synonymList.iterator().next(), Synonym.class);
1228
            if(synonymList.size() == 1){
1229
                logger.info(synonymList.size() + " Synonym found " + result.getTitleCache() );
1230
                return result;
1231
            } else {
1232
                logger.info("Several matching synonyms found. Using first: " +  result.getTitleCache());
1233
                return result;
1234
            }
1235
        }
1236
        return null;
1237
    }
1238

    
1239

    
1240
    /* (non-Javadoc)
1241
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#moveSynonymToAnotherTaxon(eu.etaxonomy.cdm.model.taxon.SynonymRelationship, eu.etaxonomy.cdm.model.taxon.Taxon, boolean, eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String, boolean)
1242
     */
1243
    @Override
1244
    public SynonymRelationship moveSynonymToAnotherTaxon(SynonymRelationship oldSynonymRelation, Taxon newTaxon, boolean moveHomotypicGroup,
1245
            SynonymRelationshipType newSynonymRelationshipType, Reference reference, String referenceDetail, boolean keepReference) throws HomotypicalGroupChangeException {
1246

    
1247
        Synonym synonym = oldSynonymRelation.getSynonym();
1248
        Taxon fromTaxon = oldSynonymRelation.getAcceptedTaxon();
1249
        //TODO what if there is no name ?? Concepts may be cached (e.g. via TCS import)
1250
        TaxonNameBase<?,?> synonymName = synonym.getName();
1251
        TaxonNameBase<?,?> fromTaxonName = fromTaxon.getName();
1252
        //set default relationship type
1253
        if (newSynonymRelationshipType == null){
1254
            newSynonymRelationshipType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
1255
        }
1256
        boolean newRelTypeIsHomotypic = newSynonymRelationshipType.equals(SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF());
1257

    
1258
        HomotypicalGroup homotypicGroup = synonymName.getHomotypicalGroup();
1259
        int hgSize = homotypicGroup.getTypifiedNames().size();
1260
        boolean isSingleInGroup = !(hgSize > 1);
1261

    
1262
        if (! isSingleInGroup){
1263
            boolean isHomotypicToAccepted = synonymName.isHomotypic(fromTaxonName);
1264
            boolean hasHomotypicSynonymRelatives = isHomotypicToAccepted ? hgSize > 2 : hgSize > 1;
1265
            if (isHomotypicToAccepted){
1266
                String message = "Synonym is in homotypic group with accepted taxon%s. First remove synonym from homotypic group of accepted taxon before moving to other taxon.";
1267
                String homotypicRelatives = hasHomotypicSynonymRelatives ? " and other synonym(s)":"";
1268
                message = String.format(message, homotypicRelatives);
1269
                throw new HomotypicalGroupChangeException(message);
1270
            }
1271
            if (! moveHomotypicGroup){
1272
                String message = "Synonym is in homotypic group with other synonym(s). Either move complete homotypic group or remove synonym from homotypic group prior to moving to other taxon.";
1273
                throw new HomotypicalGroupChangeException(message);
1274
            }
1275
        }else{
1276
            moveHomotypicGroup = true;  //single synonym always allows to moveCompleteGroup
1277
        }
1278
//        Assert.assertTrue("Synonym can only be moved with complete homotypic group", moveHomotypicGroup);
1279

    
1280
        SynonymRelationship result = null;
1281
        //move all synonyms to new taxon
1282
        List<Synonym> homotypicSynonyms = fromTaxon.getSynonymsInGroup(homotypicGroup);
1283
        for (Synonym syn: homotypicSynonyms){
1284
            Set<SynonymRelationship> synRelations = syn.getSynonymRelations();
1285
            for (SynonymRelationship synRelation : synRelations){
1286
                if (fromTaxon.equals(synRelation.getAcceptedTaxon())){
1287
                    Reference<?> newReference = reference;
1288
                    if (newReference == null && keepReference){
1289
                        newReference = synRelation.getCitation();
1290
                    }
1291
                    String newRefDetail = referenceDetail;
1292
                    if (newRefDetail == null && keepReference){
1293
                        newRefDetail = synRelation.getCitationMicroReference();
1294
                    }
1295
                    SynonymRelationship newSynRelation = newTaxon.addSynonym(syn, newSynonymRelationshipType, newReference, newRefDetail);
1296
                    fromTaxon.removeSynonymRelation(synRelation, false);
1297
//
1298
                    //change homotypic group of synonym if relType is 'homotypic'
1299
//                	if (newRelTypeIsHomotypic){
1300
//                		newTaxon.getName().getHomotypicalGroup().addTypifiedName(syn.getName());
1301
//                	}
1302
                    //set result
1303
                    if (synRelation.equals(oldSynonymRelation)){
1304
                        result = newSynRelation;
1305
                    }
1306
                }
1307
            }
1308

    
1309
        }
1310
        saveOrUpdate(newTaxon);
1311
        //Assert that there is a result
1312
        if (result == null){
1313
            String message = "Old synonym relation could not be transformed into new relation. This should not happen.";
1314
            throw new IllegalStateException(message);
1315
        }
1316
        return result;
1317
    }
1318

    
1319
    /* (non-Javadoc)
1320
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheTaxon()
1321
     */
1322
    @Override
1323
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {
1324
        return dao.getUuidAndTitleCacheTaxon();
1325
    }
1326

    
1327
    /* (non-Javadoc)
1328
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#getUuidAndTitleCacheSynonym()
1329
     */
1330
    @Override
1331
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {
1332
        return dao.getUuidAndTitleCacheSynonym();
1333
    }
1334

    
1335
    /* (non-Javadoc)
1336
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findByFullText(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
1337
     */
1338
    @Override
1339
    public Pager<SearchResult<TaxonBase>> findByFullText(
1340
            Class<? extends TaxonBase> clazz, String queryString,
1341
            Classification classification, List<Language> languages,
1342
            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
1343

    
1344

    
1345
        LuceneSearch luceneSearch = prepareFindByFullTextSearch(clazz, queryString, classification, languages, highlightFragments);
1346

    
1347
        // --- execute search
1348
        TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
1349

    
1350
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
1351
        idFieldMap.put(CdmBaseType.TAXON, "id");
1352

    
1353
        // ---  initialize taxa, thighlight matches ....
1354
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
1355
        List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
1356
                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
1357

    
1358
        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupedHitCount : 0;
1359
        return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
1360
    }
1361

    
1362
    /**
1363
     * @param clazz
1364
     * @param queryString
1365
     * @param classification
1366
     * @param languages
1367
     * @param highlightFragments
1368
     * @param directorySelectClass
1369
     * @return
1370
     */
1371
    protected LuceneSearch prepareFindByFullTextSearch(Class<? extends CdmBase> clazz, String queryString, Classification classification, List<Language> languages,
1372
            boolean highlightFragments) {
1373
        BooleanQuery finalQuery = new BooleanQuery();
1374
        BooleanQuery textQuery = new BooleanQuery();
1375

    
1376
        LuceneSearch luceneSearch = new LuceneSearch(getSession(), TaxonBase.class);
1377
        QueryFactory queryFactory = new QueryFactory(luceneSearch);
1378

    
1379
        SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("titleCache__sort", SortField.STRING,  false)};
1380
        luceneSearch.setSortFields(sortFields);
1381

    
1382
        // ---- search criteria
1383
        luceneSearch.setClazz(clazz);
1384

    
1385
        textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
1386
        textQuery.add(queryFactory.newDefinedTermQuery("name.rank", queryString, languages), Occur.SHOULD);
1387

    
1388
        finalQuery.add(textQuery, Occur.MUST);
1389

    
1390
        if(classification != null){
1391
            finalQuery.add(queryFactory.newEntityIdQuery("taxonNodes.classification.id", classification), Occur.MUST);
1392
        }
1393
        luceneSearch.setQuery(finalQuery);
1394

    
1395
        if(highlightFragments){
1396
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
1397
        }
1398
        return luceneSearch;
1399
    }
1400

    
1401

    
1402
    /* (non-Javadoc)
1403
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#findByDescriptionElementFullText(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.taxon.Classification, java.util.List, java.util.List, boolean, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
1404
     */
1405
    @Override
1406
    public Pager<SearchResult<TaxonBase>> findByDescriptionElementFullText(
1407
            Class<? extends DescriptionElementBase> clazz, String queryString,
1408
            Classification classification, List<Feature> features, List<Language> languages,
1409
            boolean highlightFragments, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
1410

    
1411

    
1412
        LuceneSearch luceneSearch = prepareByDescriptionElementFullTextSearch(clazz, queryString, classification, features, languages, highlightFragments);
1413

    
1414
        // --- execute search
1415
        TopGroupsWithMaxScore topDocsResultSet = luceneSearch.executeSearch(pageSize, pageNumber);
1416

    
1417
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
1418
        idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
1419

    
1420
        // --- initialize taxa, highlight matches ....
1421
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(luceneSearch, luceneSearch.getQuery());
1422
        @SuppressWarnings("rawtypes")
1423
        List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
1424
                topDocsResultSet, luceneSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
1425

    
1426
        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupCount : 0;
1427
        return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
1428

    
1429
    }
1430

    
1431

    
1432
    @Override
1433
    public Pager<SearchResult<TaxonBase>> findByEverythingFullText(String queryString,
1434
            Classification classification, List<Language> languages, boolean highlightFragments,
1435
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws CorruptIndexException, IOException, ParseException {
1436

    
1437
        LuceneSearch luceneSearchByDescriptionElement = prepareByDescriptionElementFullTextSearch(null, queryString, classification, null, languages, highlightFragments);
1438
        LuceneSearch luceneSearchByTaxonBase = prepareFindByFullTextSearch(null, queryString, classification, languages, highlightFragments);
1439

    
1440
        LuceneMultiSearch multiSearch = new LuceneMultiSearch(luceneSearchByDescriptionElement, luceneSearchByTaxonBase);
1441

    
1442
        // --- execute search
1443
        TopGroupsWithMaxScore topDocsResultSet = multiSearch.executeSearch(pageSize, pageNumber);
1444

    
1445
        // --- initialize taxa, highlight matches ....
1446
        ISearchResultBuilder searchResultBuilder = new SearchResultBuilder(multiSearch, multiSearch.getQuery());
1447

    
1448
        Map<CdmBaseType, String> idFieldMap = new HashMap<CdmBaseType, String>();
1449
        idFieldMap.put(CdmBaseType.TAXON, "id");
1450
        idFieldMap.put(CdmBaseType.DESCRIPTION_ELEMENT, "inDescription.taxon.id");
1451

    
1452
        List<SearchResult<TaxonBase>> searchResults = searchResultBuilder.createResultSet(
1453
                topDocsResultSet, multiSearch.getHighlightFields(), dao, idFieldMap, propertyPaths);
1454

    
1455
        int totalHits = topDocsResultSet != null ? topDocsResultSet.topGroups.totalGroupedHitCount : 0;
1456
        return new DefaultPagerImpl<SearchResult<TaxonBase>>(pageNumber, totalHits, pageSize, searchResults);
1457

    
1458
    }
1459

    
1460

    
1461
    /**
1462
     * @param clazz
1463
     * @param queryString
1464
     * @param classification
1465
     * @param features
1466
     * @param languages
1467
     * @param highlightFragments
1468
     * @param directorySelectClass
1469
     * @return
1470
     */
1471
    protected LuceneSearch prepareByDescriptionElementFullTextSearch(Class<? extends CdmBase> clazz, String queryString, Classification classification, List<Feature> features,
1472
            List<Language> languages, boolean highlightFragments) {
1473
        BooleanQuery finalQuery = new BooleanQuery();
1474
        BooleanQuery textQuery = new BooleanQuery();
1475

    
1476
        LuceneSearch luceneSearch = new LuceneSearch(getSession(), GroupByTaxonClassBridge.GROUPBY_TAXON_FIELD, DescriptionElementBase.class);
1477
        QueryFactory queryFactory = new QueryFactory(luceneSearch);
1478

    
1479
        SortField[] sortFields = new  SortField[]{SortField.FIELD_SCORE, new SortField("inDescription.taxon.titleCache__sort", SortField.STRING, false)};
1480
        luceneSearch.setSortFields(sortFields);
1481

    
1482
        // ---- search criteria
1483
        luceneSearch.setClazz(clazz);
1484
        textQuery.add(queryFactory.newTermQuery("titleCache", queryString), Occur.SHOULD);
1485

    
1486
        // common name
1487
        Query nameQuery;
1488
        if(languages == null || languages.size() == 0){
1489
            nameQuery = queryFactory.newTermQuery("name", queryString);
1490
        } else {
1491
            nameQuery = new BooleanQuery();
1492
            BooleanQuery languageSubQuery = new BooleanQuery();
1493
            for(Language lang : languages){
1494
                languageSubQuery.add(queryFactory.newTermQuery("language.uuid",  lang.getUuid().toString(), false), Occur.SHOULD);
1495
            }
1496
            ((BooleanQuery) nameQuery).add(queryFactory.newTermQuery("name", queryString), Occur.MUST);
1497
            ((BooleanQuery) nameQuery).add(languageSubQuery, Occur.MUST);
1498
        }
1499
        textQuery.add(nameQuery, Occur.SHOULD);
1500

    
1501

    
1502
        // text field from TextData
1503
        textQuery.add(queryFactory.newMultilanguageTextQuery("text", queryString, languages), Occur.SHOULD);
1504

    
1505
        // --- TermBase fields - by representation ----
1506
        // state field from CategoricalData
1507
        textQuery.add(queryFactory.newDefinedTermQuery("states.state", queryString, languages), Occur.SHOULD);
1508

    
1509
        // state field from CategoricalData
1510
        textQuery.add(queryFactory.newDefinedTermQuery("states.modifyingText", queryString, languages), Occur.SHOULD);
1511

    
1512
        // area field from Distribution
1513
        textQuery.add(queryFactory.newDefinedTermQuery("area", queryString, languages), Occur.SHOULD);
1514

    
1515
        // status field from Distribution
1516
        textQuery.add(queryFactory.newDefinedTermQuery("status", queryString, languages), Occur.SHOULD);
1517

    
1518
        finalQuery.add(textQuery, Occur.MUST);
1519
        // --- classification ----
1520

    
1521
        if(classification != null){
1522
            finalQuery.add(queryFactory.newEntityIdQuery("inDescription.taxon.taxonNodes.classification.id", classification), Occur.MUST);
1523
        }
1524

    
1525
        // --- IdentifieableEntity fields - by uuid
1526
        if(features != null && features.size() > 0 ){
1527
            finalQuery.add(queryFactory.newEntityUuidQuery("feature.uuid", features), Occur.MUST);
1528
        }
1529

    
1530
        // the description must be associated with a taxon
1531
        finalQuery.add(queryFactory.newIsNotNullQuery("inDescription.taxon.id"), Occur.MUST);
1532

    
1533
        logger.info("prepareByDescriptionElementFullTextSearch() query: " + finalQuery.toString());
1534
        luceneSearch.setQuery(finalQuery);
1535

    
1536
        if(highlightFragments){
1537
            luceneSearch.setHighlightFields(queryFactory.getTextFieldNamesAsArray());
1538
        }
1539
        return luceneSearch;
1540
    }
1541

    
1542
    /**
1543
     * DefinedTerm representations and MultilanguageString maps are stored in the Lucene index by the {@link DefinedTermBaseClassBridge}
1544
     * and {@link MultilanguageTextFieldBridge } in a consistent way. One field per language and also in one additional field for all languages.
1545
     * This method is a convenient means to retrieve a Lucene query string for such the fields.
1546
     *
1547
     * @param name name of the term field as in the Lucene index. Must be field created by {@link DefinedTermBaseClassBridge}
1548
     * or {@link MultilanguageTextFieldBridge }
1549
     * @param languages the languages to search for exclusively. Can be <code>null</code> to search in all languages
1550
     * @param stringBuilder a StringBuilder to be reused, if <code>null</code> a new StringBuilder will be instantiated and is returned
1551
     * @return the StringBuilder given a parameter or a new one if the stringBuilder parameter was null.
1552
     *
1553
     * TODO move to utiliy class !!!!!!!!
1554
     */
1555
    private StringBuilder appendLocalizedFieldQuery(String name, List<Language> languages, StringBuilder stringBuilder) {
1556

    
1557
        if(stringBuilder == null){
1558
            stringBuilder = new StringBuilder();
1559
        }
1560
        if(languages == null || languages.size() == 0){
1561
            stringBuilder.append(name + ".ALL:(%1$s) ");
1562
        } else {
1563
            for(Language lang : languages){
1564
                stringBuilder.append(name + "." + lang.getUuid().toString() + ":(%1$s) ");
1565
            }
1566
        }
1567
        return stringBuilder;
1568
    }
1569

    
1570
    @Override
1571
    public List<Synonym> createInferredSynonyms(Taxon taxon, Classification classification, SynonymRelationshipType type, boolean doWithMisappliedNames){
1572
        List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
1573
        List<Synonym> inferredSynonymsToBeRemoved = new ArrayList<Synonym>();
1574

    
1575
        HashMap <UUID, ZoologicalName> zooHashMap = new HashMap<UUID, ZoologicalName>();
1576

    
1577

    
1578
        UUID nameUuid= taxon.getName().getUuid();
1579
        ZoologicalName taxonName = getZoologicalName(nameUuid, zooHashMap);
1580
        String epithetOfTaxon = null;
1581
        String infragenericEpithetOfTaxon = null;
1582
        String infraspecificEpithetOfTaxon = null;
1583
        if (taxonName.isSpecies()){
1584
             epithetOfTaxon= taxonName.getSpecificEpithet();
1585
        } else if (taxonName.isInfraGeneric()){
1586
            infragenericEpithetOfTaxon = taxonName.getInfraGenericEpithet();
1587
        } else if (taxonName.isInfraSpecific()){
1588
            infraspecificEpithetOfTaxon = taxonName.getInfraSpecificEpithet();
1589
        }
1590
        String genusOfTaxon = taxonName.getGenusOrUninomial();
1591
        Set<TaxonNode> nodes = taxon.getTaxonNodes();
1592
         List<String> taxonNames = new ArrayList<String>();
1593

    
1594
        for (TaxonNode node: nodes){
1595
           // HashMap<String, String> synonymsGenus = new HashMap<String, String>(); // Changed this to be able to store the idInSource to a genusName
1596
           // List<String> synonymsEpithet = new ArrayList<String>();
1597

    
1598
            if (node.getClassification().equals(classification)){
1599
                if (!node.isTopmostNode()){
1600
                    TaxonNode parent = node.getParent();
1601
                    parent = (TaxonNode)HibernateProxyHelper.deproxy(parent);
1602
                    TaxonNameBase<?,?> parentName =  parent.getTaxon().getName();
1603
                    ZoologicalName zooParentName = HibernateProxyHelper.deproxy(parentName, ZoologicalName.class);
1604
                    Taxon parentTaxon = (Taxon)HibernateProxyHelper.deproxy(parent.getTaxon());
1605
                    Rank rankOfTaxon = taxonName.getRank();
1606

    
1607

    
1608
                    //create inferred synonyms for species, subspecies
1609
                    if ((parentName.isGenus() || parentName.isSpecies() || parentName.getRank().equals(Rank.SUBGENUS())) ){
1610

    
1611
                        Synonym inferredEpithet = null;
1612
                        Synonym inferredGenus = null;
1613
                        Synonym potentialCombination = null;
1614

    
1615
                        List<String> propertyPaths = new ArrayList<String>();
1616
                        propertyPaths.add("synonym");
1617
                        propertyPaths.add("synonym.name");
1618
                        List<OrderHint> orderHints = new ArrayList<OrderHint>();
1619
                        orderHints.add(new OrderHint("relatedFrom.titleCache", SortOrder.ASCENDING));
1620

    
1621
                        List<SynonymRelationship> synonymRelationshipsOfParent = dao.getSynonyms(parentTaxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1622
                        List<SynonymRelationship> synonymRelationshipsOfTaxon= dao.getSynonyms(taxon, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), null, null,orderHints,propertyPaths);
1623

    
1624
                        List<TaxonRelationship> taxonRelListParent = null;
1625
                        List<TaxonRelationship> taxonRelListTaxon = null;
1626
                        if (doWithMisappliedNames){
1627
                            taxonRelListParent = dao.getTaxonRelationships(parentTaxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1628
                            taxonRelListTaxon = dao.getTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, orderHints, propertyPaths, Direction.relatedTo);
1629
                        }
1630

    
1631

    
1632
                        if (type.equals(SynonymRelationshipType.INFERRED_EPITHET_OF())){
1633

    
1634

    
1635
                            for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1636
                                Synonym syn = synonymRelationOfParent.getSynonym();
1637

    
1638
                                inferredEpithet = createInferredEpithets(taxon,
1639
                                        zooHashMap, taxonName, epithetOfTaxon,
1640
                                        infragenericEpithetOfTaxon,
1641
                                        infraspecificEpithetOfTaxon,
1642
                                        taxonNames, parentName,
1643
                                        syn);
1644

    
1645

    
1646
                                inferredSynonyms.add(inferredEpithet);
1647
                                zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1648
                                taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1649
                            }
1650

    
1651
                            if (doWithMisappliedNames){
1652

    
1653
                                for (TaxonRelationship taxonRelationship: taxonRelListParent){
1654
                                     Taxon misappliedName = taxonRelationship.getFromTaxon();
1655

    
1656
                                     inferredEpithet = createInferredEpithets(taxon,
1657
                                             zooHashMap, taxonName, epithetOfTaxon,
1658
                                             infragenericEpithetOfTaxon,
1659
                                             infraspecificEpithetOfTaxon,
1660
                                             taxonNames, parentName,
1661
                                             misappliedName);
1662

    
1663
                                    inferredSynonyms.add(inferredEpithet);
1664
                                    zooHashMap.put(inferredEpithet.getName().getUuid(), (ZoologicalName)inferredEpithet.getName());
1665
                                     taxonNames.add(((ZoologicalName)inferredEpithet.getName()).getNameCache());
1666
                                }
1667
                            }
1668

    
1669
                            if (!taxonNames.isEmpty()){
1670
                            List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1671
                            ZoologicalName name;
1672
                            if (!synNotInCDM.isEmpty()){
1673
                                inferredSynonymsToBeRemoved.clear();
1674

    
1675
                                for (Synonym syn :inferredSynonyms){
1676
                                    name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1677
                                    if (!synNotInCDM.contains(name.getNameCache())){
1678
                                        inferredSynonymsToBeRemoved.add(syn);
1679
                                    }
1680
                                }
1681

    
1682
                                // Remove identified Synonyms from inferredSynonyms
1683
                                for (Synonym synonym : inferredSynonymsToBeRemoved) {
1684
                                    inferredSynonyms.remove(synonym);
1685
                                }
1686
                            }
1687
                        }
1688

    
1689
                    }else if (type.equals(SynonymRelationshipType.INFERRED_GENUS_OF())){
1690

    
1691

    
1692
                        for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1693
                            TaxonNameBase synName;
1694
                            ZoologicalName inferredSynName;
1695

    
1696
                            Synonym syn = synonymRelationOfTaxon.getSynonym();
1697
                            inferredGenus = createInferredGenus(taxon,
1698
                                    zooHashMap, taxonName, epithetOfTaxon,
1699
                                    genusOfTaxon, taxonNames, zooParentName, syn);
1700

    
1701
                            inferredSynonyms.add(inferredGenus);
1702
                            zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
1703
                            taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
1704

    
1705

    
1706
                        }
1707

    
1708
                        if (doWithMisappliedNames){
1709

    
1710
                            for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1711
                                Taxon misappliedName = taxonRelationship.getFromTaxon();
1712
                                inferredGenus = createInferredGenus(taxon, zooHashMap, taxonName, infraspecificEpithetOfTaxon, genusOfTaxon, taxonNames, zooParentName,  misappliedName);
1713

    
1714
                                inferredSynonyms.add(inferredGenus);
1715
                                zooHashMap.put(inferredGenus.getName().getUuid(), (ZoologicalName)inferredGenus.getName());
1716
                                 taxonNames.add(( (ZoologicalName)inferredGenus.getName()).getNameCache());
1717
                            }
1718
                        }
1719

    
1720

    
1721
                        if (!taxonNames.isEmpty()){
1722
                            List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1723
                            ZoologicalName name;
1724
                            if (!synNotInCDM.isEmpty()){
1725
                                inferredSynonymsToBeRemoved.clear();
1726

    
1727
                                for (Synonym syn :inferredSynonyms){
1728
                                    name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1729
                                    if (!synNotInCDM.contains(name.getNameCache())){
1730
                                        inferredSynonymsToBeRemoved.add(syn);
1731
                                    }
1732
                                }
1733

    
1734
                                // Remove identified Synonyms from inferredSynonyms
1735
                                for (Synonym synonym : inferredSynonymsToBeRemoved) {
1736
                                    inferredSynonyms.remove(synonym);
1737
                                }
1738
                            }
1739
                        }
1740

    
1741
                    }else if (type.equals(SynonymRelationshipType.POTENTIAL_COMBINATION_OF())){
1742

    
1743
                        Reference sourceReference = null; // TODO: Determination of sourceReference is redundant
1744
                        ZoologicalName inferredSynName;
1745
                        //for all synonyms of the parent...
1746
                        for (SynonymRelationship synonymRelationOfParent:synonymRelationshipsOfParent){
1747
                            TaxonNameBase synName;
1748
                            Synonym synParent = synonymRelationOfParent.getSynonym();
1749
                            synName = synParent.getName();
1750

    
1751
                            HibernateProxyHelper.deproxy(synParent);
1752

    
1753
                            // Set the sourceReference
1754
                            sourceReference = synParent.getSec();
1755

    
1756
                            // Determine the idInSource
1757
                            String idInSourceParent = getIdInSource(synParent);
1758

    
1759
                            ZoologicalName parentSynZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1760
                            String synParentGenus = parentSynZooName.getGenusOrUninomial();
1761
                            String synParentInfragenericName = null;
1762
                            String synParentSpecificEpithet = null;
1763

    
1764
                            if (parentSynZooName.isInfraGeneric()){
1765
                                synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1766
                            }
1767
                            if (parentSynZooName.isSpecies()){
1768
                                synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1769
                            }
1770

    
1771
                           /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
1772
                                synonymsGenus.put(synGenusName, idInSource);
1773
                            }*/
1774

    
1775
                            //for all synonyms of the taxon
1776

    
1777
                            for (SynonymRelationship synonymRelationOfTaxon:synonymRelationshipsOfTaxon){
1778

    
1779
                                Synonym syn = synonymRelationOfTaxon.getSynonym();
1780
                                ZoologicalName zooSynName = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1781
                                potentialCombination = createPotentialCombination(idInSourceParent, parentSynZooName, zooSynName,
1782
                                        synParentGenus,
1783
                                        synParentInfragenericName,
1784
                                        synParentSpecificEpithet, syn, zooHashMap);
1785

    
1786
                                taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1787
                                inferredSynonyms.add(potentialCombination);
1788
                                zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1789
                                 taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1790

    
1791
                            }
1792

    
1793

    
1794
                        }
1795

    
1796
                        if (doWithMisappliedNames){
1797

    
1798
                            for (TaxonRelationship parentRelationship: taxonRelListParent){
1799

    
1800
                                TaxonNameBase misappliedParentName;
1801

    
1802
                                Taxon misappliedParent = parentRelationship.getFromTaxon();
1803
                                misappliedParentName = misappliedParent.getName();
1804

    
1805
                                HibernateProxyHelper.deproxy(misappliedParent);
1806

    
1807
                                // Set the sourceReference
1808
                                sourceReference = misappliedParent.getSec();
1809

    
1810
                                // Determine the idInSource
1811
                                String idInSourceParent = getIdInSource(misappliedParent);
1812

    
1813
                                ZoologicalName parentSynZooName = getZoologicalName(misappliedParentName.getUuid(), zooHashMap);
1814
                                String synParentGenus = parentSynZooName.getGenusOrUninomial();
1815
                                String synParentInfragenericName = null;
1816
                                String synParentSpecificEpithet = null;
1817

    
1818
                                if (parentSynZooName.isInfraGeneric()){
1819
                                    synParentInfragenericName = parentSynZooName.getInfraGenericEpithet();
1820
                                }
1821
                                if (parentSynZooName.isSpecies()){
1822
                                    synParentSpecificEpithet = parentSynZooName.getSpecificEpithet();
1823
                                }
1824

    
1825

    
1826
                                for (TaxonRelationship taxonRelationship: taxonRelListTaxon){
1827
                                    Taxon misappliedName = taxonRelationship.getFromTaxon();
1828
                                    ZoologicalName zooMisappliedName = getZoologicalName(misappliedName.getName().getUuid(), zooHashMap);
1829
                                    potentialCombination = createPotentialCombination(
1830
                                            idInSourceParent, parentSynZooName, zooMisappliedName,
1831
                                            synParentGenus,
1832
                                            synParentInfragenericName,
1833
                                            synParentSpecificEpithet, misappliedName, zooHashMap);
1834

    
1835

    
1836
                                    taxon.addSynonym(potentialCombination, SynonymRelationshipType.POTENTIAL_COMBINATION_OF());
1837
                                    inferredSynonyms.add(potentialCombination);
1838
                                    zooHashMap.put(potentialCombination.getName().getUuid(), (ZoologicalName)potentialCombination.getName());
1839
                                     taxonNames.add(( (ZoologicalName)potentialCombination.getName()).getNameCache());
1840
                                }
1841
                            }
1842
                        }
1843

    
1844
                        if (!taxonNames.isEmpty()){
1845
                            List<String> synNotInCDM = dao.taxaByNameNotInDB(taxonNames);
1846
                            ZoologicalName name;
1847
                            if (!synNotInCDM.isEmpty()){
1848
                                inferredSynonymsToBeRemoved.clear();
1849
                                for (Synonym syn :inferredSynonyms){
1850
                                    try{
1851
                                        name = (ZoologicalName) syn.getName();
1852
                                    }catch (ClassCastException e){
1853
                                        name = getZoologicalName(syn.getName().getUuid(), zooHashMap);
1854
                                    }
1855
                                    if (!synNotInCDM.contains(name.getNameCache())){
1856
                                        inferredSynonymsToBeRemoved.add(syn);
1857
                                    }
1858
                                 }
1859
                                // Remove identified Synonyms from inferredSynonyms
1860
                                for (Synonym synonym : inferredSynonymsToBeRemoved) {
1861
                                    inferredSynonyms.remove(synonym);
1862
                                }
1863
                            }
1864
                         }
1865
                        }
1866
                    }else {
1867
                        logger.info("The synonymrelationship type is not defined.");
1868
                        return inferredSynonyms;
1869
                    }
1870
                }
1871
            }
1872

    
1873
        }
1874

    
1875
        return inferredSynonyms;
1876
    }
1877

    
1878
    private Synonym createPotentialCombination(String idInSourceParent,
1879
            ZoologicalName parentSynZooName, 	ZoologicalName zooSynName, String synParentGenus,
1880
            String synParentInfragenericName, String synParentSpecificEpithet,
1881
            TaxonBase syn, HashMap<UUID, ZoologicalName> zooHashMap) {
1882
        Synonym potentialCombination;
1883
        Reference sourceReference;
1884
        ZoologicalName inferredSynName;
1885
        HibernateProxyHelper.deproxy(syn);
1886

    
1887
        // Set sourceReference
1888
        sourceReference = syn.getSec();
1889
        if (sourceReference == null){
1890
            logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
1891
            //TODO:Remove
1892
            if (!parentSynZooName.getTaxa().isEmpty()){
1893
                TaxonBase taxon = parentSynZooName.getTaxa().iterator().next();
1894

    
1895
                sourceReference = taxon.getSec();
1896
            }
1897
        }
1898
        String synTaxonSpecificEpithet = zooSynName.getSpecificEpithet();
1899

    
1900
        String synTaxonInfraSpecificName= null;
1901

    
1902
        if (parentSynZooName.isSpecies()){
1903
            synTaxonInfraSpecificName = zooSynName.getInfraSpecificEpithet();
1904
        }
1905

    
1906
        /*if (epithetName != null && !synonymsEpithet.contains(epithetName)){
1907
            synonymsEpithet.add(epithetName);
1908
        }*/
1909

    
1910
        //create potential combinations...
1911
        inferredSynName = ZoologicalName.NewInstance(syn.getName().getRank());
1912

    
1913
        inferredSynName.setGenusOrUninomial(synParentGenus);
1914
        if (zooSynName.isSpecies()){
1915
              inferredSynName.setSpecificEpithet(synTaxonSpecificEpithet);
1916
              if (parentSynZooName.isInfraGeneric()){
1917
                  inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1918
              }
1919
        }
1920
        if (zooSynName.isInfraSpecific()){
1921
            inferredSynName.setSpecificEpithet(synParentSpecificEpithet);
1922
            inferredSynName.setInfraSpecificEpithet(synTaxonInfraSpecificName);
1923
        }
1924
        if (parentSynZooName.isInfraGeneric()){
1925
            inferredSynName.setInfraGenericEpithet(synParentInfragenericName);
1926
        }
1927

    
1928

    
1929
        potentialCombination = Synonym.NewInstance(inferredSynName, null);
1930

    
1931
        // Set the sourceReference
1932
        potentialCombination.setSec(sourceReference);
1933

    
1934

    
1935
        // Determine the idInSource
1936
        String idInSourceSyn= getIdInSource(syn);
1937

    
1938
        if (idInSourceParent != null && idInSourceSyn != null) {
1939
            IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1940
            inferredSynName.addSource(originalSource);
1941
            originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceParent, POTENTIAL_COMBINATION_NAMESPACE, sourceReference, null);
1942
            potentialCombination.addSource(originalSource);
1943
        }
1944

    
1945
        inferredSynName.generateTitle();
1946

    
1947
        return potentialCombination;
1948
    }
1949

    
1950
    private Synonym createInferredGenus(Taxon taxon,
1951
            HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
1952
            String epithetOfTaxon, String genusOfTaxon,
1953
            List<String> taxonNames, ZoologicalName zooParentName,
1954
            TaxonBase syn) {
1955

    
1956
        Synonym inferredGenus;
1957
        TaxonNameBase synName;
1958
        ZoologicalName inferredSynName;
1959
        synName =syn.getName();
1960
        HibernateProxyHelper.deproxy(syn);
1961

    
1962
        // Determine the idInSource
1963
        String idInSourceSyn = getIdInSource(syn);
1964
        String idInSourceTaxon = getIdInSource(taxon);
1965
        // Determine the sourceReference
1966
        Reference sourceReference = syn.getSec();
1967

    
1968
        //logger.warn(sourceReference.getTitleCache());
1969

    
1970
        synName = syn.getName();
1971
        ZoologicalName synZooName = getZoologicalName(synName.getUuid(), zooHashMap);
1972
        String synSpeciesEpithetName = synZooName.getSpecificEpithet();
1973
                     /* if (synonymsEpithet != null && !synonymsEpithet.contains(synSpeciesEpithetName)){
1974
            synonymsEpithet.add(synSpeciesEpithetName);
1975
        }*/
1976

    
1977
        inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
1978
        //TODO:differ between parent is genus and taxon is species, parent is subgenus and taxon is species, parent is species and taxon is subspecies and parent is genus and taxon is subgenus...
1979

    
1980

    
1981
        inferredSynName.setGenusOrUninomial(genusOfTaxon);
1982
        if (zooParentName.isInfraGeneric()){
1983
            inferredSynName.setInfraGenericEpithet(zooParentName.getInfraGenericEpithet());
1984
        }
1985

    
1986
        if (taxonName.isSpecies()){
1987
            inferredSynName.setSpecificEpithet(synSpeciesEpithetName);
1988
        }
1989
        if (taxonName.isInfraSpecific()){
1990
            inferredSynName.setSpecificEpithet(epithetOfTaxon);
1991
            inferredSynName.setInfraSpecificEpithet(synZooName.getInfraGenericEpithet());
1992
        }
1993

    
1994

    
1995
        inferredGenus = Synonym.NewInstance(inferredSynName, null);
1996

    
1997
        // Set the sourceReference
1998
        inferredGenus.setSec(sourceReference);
1999

    
2000
        // Add the original source
2001
        if (idInSourceSyn != null && idInSourceTaxon != null) {
2002
            IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2003
            inferredGenus.addSource(originalSource);
2004

    
2005
            originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2006
            inferredSynName.addSource(originalSource);
2007
            originalSource = null;
2008

    
2009
        }else{
2010
            logger.error("There is an idInSource missing: " + idInSourceSyn + " of Synonym or " + idInSourceTaxon + " of Taxon");
2011
            IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2012
            inferredGenus.addSource(originalSource);
2013

    
2014
            originalSource = IdentifiableSource.NewInstance(idInSourceSyn + "; " + idInSourceTaxon, INFERRED_GENUS_NAMESPACE, sourceReference, null);
2015
            inferredSynName.addSource(originalSource);
2016
            originalSource = null;
2017
        }
2018

    
2019
        taxon.addSynonym(inferredGenus, SynonymRelationshipType.INFERRED_GENUS_OF());
2020

    
2021
        inferredSynName.generateTitle();
2022

    
2023

    
2024
        return inferredGenus;
2025
    }
2026

    
2027
    private Synonym createInferredEpithets(Taxon taxon,
2028
            HashMap<UUID, ZoologicalName> zooHashMap, ZoologicalName taxonName,
2029
            String epithetOfTaxon, String infragenericEpithetOfTaxon,
2030
            String infraspecificEpithetOfTaxon, List<String> taxonNames,
2031
            TaxonNameBase parentName, TaxonBase syn) {
2032

    
2033
        Synonym inferredEpithet;
2034
        TaxonNameBase<?,?> synName;
2035
        ZoologicalName inferredSynName;
2036
        HibernateProxyHelper.deproxy(syn);
2037

    
2038
        // Determine the idInSource
2039
        String idInSourceSyn = getIdInSource(syn);
2040
        String idInSourceTaxon =  getIdInSource(taxon);
2041
        // Determine the sourceReference
2042
        Reference<?> sourceReference = syn.getSec();
2043

    
2044
        if (sourceReference == null){
2045
            logger.warn("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon");
2046
            //TODO:Remove
2047
            System.out.println("The synonym has no sec reference because it is a misapplied name! Take the sec reference of taxon" + taxon.getSec());
2048
            sourceReference = taxon.getSec();
2049
        }
2050

    
2051
        synName = syn.getName();
2052
        ZoologicalName zooSynName = getZoologicalName(synName.getUuid(), zooHashMap);
2053
        String synGenusName = zooSynName.getGenusOrUninomial();
2054
        String synInfraGenericEpithet = null;
2055
        String synSpecificEpithet = null;
2056

    
2057
        if (zooSynName.getInfraGenericEpithet() != null){
2058
            synInfraGenericEpithet = zooSynName.getInfraGenericEpithet();
2059
        }
2060

    
2061
        if (zooSynName.isInfraSpecific()){
2062
            synSpecificEpithet = zooSynName.getSpecificEpithet();
2063
        }
2064

    
2065
                     /* if (synGenusName != null && !synonymsGenus.containsKey(synGenusName)){
2066
            synonymsGenus.put(synGenusName, idInSource);
2067
        }*/
2068

    
2069
        inferredSynName = ZoologicalName.NewInstance(taxon.getName().getRank());
2070

    
2071
        // DEBUG TODO: for subgenus or subspecies the infrageneric or infraspecific epithet should be used!!!
2072
        if (epithetOfTaxon == null && infragenericEpithetOfTaxon == null && infraspecificEpithetOfTaxon == null) {
2073
            logger.error("This specificEpithet is NULL" + taxon.getTitleCache());
2074
        }
2075
        inferredSynName.setGenusOrUninomial(synGenusName);
2076

    
2077
        if (parentName.isInfraGeneric()){
2078
            inferredSynName.setInfraGenericEpithet(synInfraGenericEpithet);
2079
        }
2080
        if (taxonName.isSpecies()){
2081
            inferredSynName.setSpecificEpithet(epithetOfTaxon);
2082
        }else if (taxonName.isInfraSpecific()){
2083
            inferredSynName.setSpecificEpithet(synSpecificEpithet);
2084
            inferredSynName.setInfraSpecificEpithet(infraspecificEpithetOfTaxon);
2085
        }
2086

    
2087
        inferredEpithet = Synonym.NewInstance(inferredSynName, null);
2088

    
2089
        // Set the sourceReference
2090
        inferredEpithet.setSec(sourceReference);
2091

    
2092
        /* Add the original source
2093
        if (idInSource != null) {
2094
            IdentifiableSource originalSource = IdentifiableSource.NewInstance(idInSource, "InferredEpithetOf", syn.getSec(), null);
2095

    
2096
            // Add the citation
2097
            Reference citation = getCitation(syn);
2098
            if (citation != null) {
2099
                originalSource.setCitation(citation);
2100
                inferredEpithet.addSource(originalSource);
2101
            }
2102
        }*/
2103
        String taxonId = idInSourceTaxon+ "; " + idInSourceSyn;
2104

    
2105

    
2106
        IdentifiableSource originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
2107

    
2108
        inferredEpithet.addSource(originalSource);
2109

    
2110
        originalSource = IdentifiableSource.NewInstance(taxonId, INFERRED_EPITHET_NAMESPACE, sourceReference, null);
2111

    
2112
        inferredSynName.addSource(originalSource);
2113

    
2114

    
2115

    
2116
        taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_EPITHET_OF());
2117

    
2118
        inferredSynName.generateTitle();
2119
        return inferredEpithet;
2120
    }
2121

    
2122
    /**
2123
     * Returns an existing ZoologicalName or extends an internal hashmap if it does not exist.
2124
     * Very likely only useful for createInferredSynonyms().
2125
     * @param uuid
2126
     * @param zooHashMap
2127
     * @return
2128
     */
2129
    private ZoologicalName getZoologicalName(UUID uuid, HashMap <UUID, ZoologicalName> zooHashMap) {
2130
        ZoologicalName taxonName =nameDao.findZoologicalNameByUUID(uuid);
2131
        if (taxonName == null) {
2132
            taxonName = zooHashMap.get(uuid);
2133
        }
2134
        return taxonName;
2135
    }
2136

    
2137
    /**
2138
     * Returns the idInSource for a given Synonym.
2139
     * @param syn
2140
     */
2141
    private String getIdInSource(TaxonBase taxonBase) {
2142
        String idInSource = null;
2143
        Set<IdentifiableSource> sources = taxonBase.getSources();
2144
        if (sources.size() == 1) {
2145
            IdentifiableSource source = sources.iterator().next();
2146
            if (source != null) {
2147
                idInSource  = source.getIdInSource();
2148
            }
2149
        } else if (sources.size() > 1) {
2150
            int count = 1;
2151
            idInSource = "";
2152
            for (IdentifiableSource source : sources) {
2153
                idInSource += source.getIdInSource();
2154
                if (count < sources.size()) {
2155
                    idInSource += "; ";
2156
                }
2157
                count++;
2158
            }
2159
        } else if (sources.size() == 0){
2160
            logger.warn("No idInSource for TaxonBase " + taxonBase.getUuid() + " - " + taxonBase.getTitleCache());
2161
        }
2162

    
2163

    
2164
        return idInSource;
2165
    }
2166

    
2167

    
2168
    /**
2169
     * Returns the citation for a given Synonym.
2170
     * @param syn
2171
     */
2172
    private Reference getCitation(Synonym syn) {
2173
        Reference citation = null;
2174
        Set<IdentifiableSource> sources = syn.getSources();
2175
        if (sources.size() == 1) {
2176
            IdentifiableSource source = sources.iterator().next();
2177
            if (source != null) {
2178
                citation = source.getCitation();
2179
            }
2180
        } else if (sources.size() > 1) {
2181
            logger.warn("This Synonym has more than one source: " + syn.getUuid() + " (" + syn.getTitleCache() +")");
2182
        }
2183

    
2184
        return citation;
2185
    }
2186

    
2187
    @Override
2188
    public List<Synonym>  createAllInferredSynonyms(Taxon taxon, Classification tree, boolean doWithMisappliedNames){
2189
        List <Synonym> inferredSynonyms = new ArrayList<Synonym>();
2190

    
2191
        inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_EPITHET_OF(), doWithMisappliedNames));
2192
        inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.INFERRED_GENUS_OF(), doWithMisappliedNames));
2193
        inferredSynonyms.addAll(createInferredSynonyms(taxon, tree, SynonymRelationshipType.POTENTIAL_COMBINATION_OF(), doWithMisappliedNames));
2194

    
2195
        return inferredSynonyms;
2196
    }
2197

    
2198
    /* (non-Javadoc)
2199
     * @see eu.etaxonomy.cdm.api.service.ITaxonService#listClassifications(eu.etaxonomy.cdm.model.taxon.TaxonBase, java.lang.Integer, java.lang.Integer, java.util.List)
2200
     */
2201
    @Override
2202
    public List<Classification> listClassifications(TaxonBase taxonBase, Integer limit, Integer start, List<String> propertyPaths) {
2203

    
2204
        // TODO quickly implemented, create according dao !!!!
2205
        Set<TaxonNode> nodes = new HashSet<TaxonNode>();
2206
        Set<Classification> classifications = new HashSet<Classification>();
2207
        List<Classification> list = new ArrayList<Classification>();
2208

    
2209
        if (taxonBase == null) {
2210
            return list;
2211
        }
2212

    
2213
        taxonBase = load(taxonBase.getUuid());
2214

    
2215
        if (taxonBase instanceof Taxon) {
2216
            nodes.addAll(((Taxon)taxonBase).getTaxonNodes());
2217
        } else {
2218
            for (Taxon taxon : ((Synonym)taxonBase).getAcceptedTaxa() ) {
2219
                nodes.addAll(taxon.getTaxonNodes());
2220
            }
2221
        }
2222
        for (TaxonNode node : nodes) {
2223
            classifications.add(node.getClassification());
2224
        }
2225
        list.addAll(classifications);
2226
        return list;
2227
    }
2228

    
2229

    
2230

    
2231

    
2232

    
2233

    
2234
}
(76-76/81)