Project

General

Profile

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

    
11
import java.util.ArrayList;
12
import java.util.Collection;
13
import java.util.Collections;
14
import java.util.Comparator;
15
import java.util.HashSet;
16
import java.util.Iterator;
17
import java.util.List;
18
import java.util.Set;
19
import java.util.SortedSet;
20
import java.util.TreeSet;
21
import java.util.UUID;
22

    
23
import org.apache.commons.lang.StringUtils;
24
import org.apache.log4j.Logger;
25
import org.hibernate.Criteria;
26
import org.hibernate.FetchMode;
27
import org.hibernate.Hibernate;
28
import org.hibernate.Query;
29
import org.hibernate.Session;
30
import org.hibernate.criterion.Criterion;
31
import org.hibernate.criterion.Order;
32
import org.hibernate.criterion.Projections;
33
import org.hibernate.criterion.Restrictions;
34
import org.hibernate.envers.query.AuditEntity;
35
import org.hibernate.envers.query.AuditQuery;
36
import org.hibernate.envers.query.criteria.internal.NotNullAuditExpression;
37
import org.hibernate.envers.query.internal.property.EntityPropertyName;
38
import org.hibernate.search.FullTextSession;
39
import org.hibernate.search.Search;
40
import org.springframework.beans.factory.annotation.Autowired;
41
import org.springframework.beans.factory.annotation.Qualifier;
42
import org.springframework.dao.DataAccessException;
43
import org.springframework.stereotype.Repository;
44

    
45
import eu.etaxonomy.cdm.model.common.DefinedTerm;
46
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
47
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
48
import eu.etaxonomy.cdm.model.common.LSID;
49
import eu.etaxonomy.cdm.model.common.MarkerType;
50
import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
51
import eu.etaxonomy.cdm.model.location.NamedArea;
52
import eu.etaxonomy.cdm.model.name.Rank;
53
import eu.etaxonomy.cdm.model.name.TaxonName;
54
import eu.etaxonomy.cdm.model.name.TaxonNameComparator;
55
import eu.etaxonomy.cdm.model.reference.Reference;
56
import eu.etaxonomy.cdm.model.taxon.Classification;
57
import eu.etaxonomy.cdm.model.taxon.Synonym;
58
import eu.etaxonomy.cdm.model.taxon.SynonymType;
59
import eu.etaxonomy.cdm.model.taxon.Taxon;
60
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
61
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
62
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
63
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
64
import eu.etaxonomy.cdm.model.taxon.UuidAndTitleCacheTaxonComparator;
65
import eu.etaxonomy.cdm.model.view.AuditEvent;
66
import eu.etaxonomy.cdm.persistence.dao.hibernate.common.IdentifiableDaoBase;
67
import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
68
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
69
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
70
import eu.etaxonomy.cdm.persistence.query.MatchMode;
71
import eu.etaxonomy.cdm.persistence.query.NameSearchOrder;
72
import eu.etaxonomy.cdm.persistence.query.OrderHint;
73
import eu.etaxonomy.cdm.persistence.query.TaxonTitleType;
74

    
75

    
76
/**
77
 * @author a.mueller
78
 * @since 24.11.2008
79
 */
80
@Repository
81
@Qualifier("taxonDaoHibernateImpl")
82
public class TaxonDaoHibernateImpl
83
              extends IdentifiableDaoBase<TaxonBase>
84
              implements ITaxonDao {
85
//    private AlternativeSpellingSuggestionParser<TaxonBase> alternativeSpellingSuggestionParser;
86
    private static final Logger logger = Logger.getLogger(TaxonDaoHibernateImpl.class);
87

    
88
    public TaxonDaoHibernateImpl() {
89
        super(TaxonBase.class);
90
        indexedClasses = new Class[2];
91
        indexedClasses[0] = Taxon.class;
92
        indexedClasses[1] = Synonym.class;
93
        super.defaultField = "name.titleCache_tokenized";
94
    }
95

    
96
    @Autowired
97
    private ITaxonNameDao taxonNameDao;
98

    
99
////    spelling support currently disabled in appcontext, see spelling.xml ... "
100
////    @Autowired(required = false)   //TODO switched of because it caused problems when starting CdmApplicationController
101
//    public void setAlternativeSpellingSuggestionParser(AlternativeSpellingSuggestionParser<TaxonBase> alternativeSpellingSuggestionParser) {
102
//        this.alternativeSpellingSuggestionParser = alternativeSpellingSuggestionParser;
103
//    }
104

    
105
    @Override
106
    public TaxonBase load(UUID uuid, List<String> propertyPaths){
107
        return load(uuid, INCLUDE_UNPUBLISHED, propertyPaths);
108
    }
109

    
110
    @Override
111
    public TaxonBase load(UUID uuid, boolean includeUnpublished, List<String> propertyPaths){
112
        TaxonBase<?> result = super.load(uuid, includeUnpublished, propertyPaths);
113
        return result; //(result == null || (!result.isPublish() && !includeUnpublished))? null : result;
114
    }
115

    
116

    
117
    @Override
118
    public List<TaxonBase> getTaxaByName(String queryString, boolean includeUnpublished, Reference sec) {
119

    
120
        return getTaxaByName(queryString, true, includeUnpublished, sec);
121
    }
122

    
123
    @Override
124
    public List<TaxonBase> getTaxaByName(String queryString, Boolean accepted, boolean includeUnpublished, Reference sec) {
125
        checkNotInPriorView("TaxonDaoHibernateImpl.getTaxaByName(String name, Reference sec)");
126

    
127
        Criteria criteria = null;
128
        if (accepted == true) {
129
            criteria = getSession().createCriteria(Taxon.class);
130
        } else {
131
            criteria = getSession().createCriteria(Synonym.class);
132
        }
133

    
134
        criteria.setFetchMode( "name", FetchMode.JOIN );
135
        criteria.createAlias("name", "name");
136

    
137
        if (!includeUnpublished){
138
            criteria.add(Restrictions.eq("publish", Boolean.TRUE ));
139
        }
140

    
141
        if (sec != null && sec.getId() != 0) {
142
            criteria.add(Restrictions.eq("sec", sec ) );
143
        }
144

    
145
        if (queryString != null) {
146
            criteria.add(Restrictions.ilike("name.nameCache", queryString));
147
        }
148

    
149
        @SuppressWarnings({ "unchecked", "rawtypes" })
150
        List<TaxonBase> result = criteria.list();
151
        return result;
152
    }
153

    
154
    public List<TaxonBase> getTaxaByName(boolean doTaxa, boolean doSynonyms, boolean includeUnpublished,
155
            String queryString, MatchMode matchMode, Integer pageSize, Integer pageNumber) {
156
        return getTaxaByName(doTaxa, doSynonyms, false, false, false,
157
                queryString, null, matchMode, null, includeUnpublished, null, pageSize, pageNumber, null);
158
    }
159

    
160
    @Override
161
    public List<TaxonBase> getTaxaByName(String queryString, MatchMode matchMode,
162
            Boolean accepted, boolean includeUnpublished, Integer pageSize, Integer pageNumber) {
163

    
164
        boolean doTaxa = true;
165
        boolean doSynonyms = true;
166

    
167
        if (accepted == true) {
168
            doSynonyms = false;
169
        } else {
170
           doTaxa = false;
171
        }
172
        return getTaxaByName(doTaxa, doSynonyms, includeUnpublished, queryString, matchMode, pageSize, pageNumber);
173
    }
174

    
175
    @Override
176
    public List<TaxonBase> getTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, boolean doCommonNames,
177
            boolean includeAuthors,
178
            String queryString, Classification classification,
179
            MatchMode matchMode, Set<NamedArea> namedAreas, boolean includeUnpublished, NameSearchOrder order,
180
            Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
181

    
182
        boolean doCount = false;
183

    
184
        String searchField = includeAuthors ? "titleCache" : "nameCache";
185
        Query query = prepareTaxaByName(doTaxa, doSynonyms, doMisappliedNames, doCommonNames, includeUnpublished, searchField, queryString, classification, matchMode, namedAreas, order, pageSize, pageNumber, doCount);
186

    
187
        if (query != null){
188
            @SuppressWarnings({ "unchecked", "rawtypes" })
189
            List<TaxonBase> results = query.list();
190

    
191
            defaultBeanInitializer.initializeAll(results, propertyPaths);
192

    
193
            //Collections.sort(results, comp);
194
            return results;
195
        }else{
196
            return new ArrayList<>();
197
        }
198

    
199

    
200
    }
201

    
202

    
203
    //new search for the editor, for performance issues the return values are only uuid and titleCache, to avoid the initialisation of all objects
204
    @Override
205
    @SuppressWarnings("unchecked")
206
    public List<UuidAndTitleCache<? extends IdentifiableEntity>> getTaxaByNameForEditor(boolean doTaxa, boolean doSynonyms, boolean doNamesWithoutTaxa,
207
            boolean doMisappliedNames, boolean doCommonNames, boolean includeUnpublished, String queryString, Classification classification,
208
            MatchMode matchMode, Set<NamedArea> namedAreas, NameSearchOrder order) {
209

    
210
        if (order == null){
211
            order = NameSearchOrder.ALPHA;  //TODO add to signature
212
        }
213

    
214
        boolean doCount = false;
215
        boolean includeAuthors = false;
216
        @SuppressWarnings("rawtypes")
217
        List<UuidAndTitleCache<? extends IdentifiableEntity>> resultObjects = new ArrayList<>();
218
        if (doNamesWithoutTaxa){
219
        	List<TaxonName> nameResult = taxonNameDao.findByName(
220
        	        includeAuthors, queryString, matchMode, null, null, null, null);
221

    
222
        	for (TaxonName name: nameResult){
223
        		if (name.getTaxonBases().size() == 0){
224
        			resultObjects.add(new UuidAndTitleCache<>(TaxonName.class, name.getUuid(),
225
        			        name.getId(), name.getTitleCache()));
226
        		}
227
        	}
228
        	if (!doSynonyms && !doTaxa && !doCommonNames){
229
        		return resultObjects;
230
        	}
231
        }
232
        Query query = prepareTaxaByNameForEditor(doTaxa, doSynonyms, doMisappliedNames, doCommonNames, includeUnpublished,
233
                "nameCache", queryString, classification, matchMode, namedAreas, doCount, order);
234

    
235
        if (query != null){
236
            List<Object[]> results = query.list();
237

    
238
            Object[] result;
239
            for(int i = 0; i<results.size();i++){
240
                result = results.get(i);
241

    
242
                //differentiate taxa and synonyms
243
                // new Boolean(result[3].toString()) is due to the fact that result[3] could be a Boolean ora String
244
                // see FIXME in 'prepareQuery' for more details
245
                if (doTaxa || doSynonyms || doCommonNames){
246
                    if (result[3].equals("synonym")) {
247
                        resultObjects.add( new UuidAndTitleCache<>(Synonym.class, (UUID) result[0], (Integer) result[1], (String)result[2], new Boolean(result[4].toString()), null));
248
                    }
249
                    else {
250
                        resultObjects.add( new UuidAndTitleCache<>(Taxon.class, (UUID) result[0], (Integer) result[1], (String)result[2], new Boolean(result[4].toString()), null));
251
                    }
252

    
253
                }else if (doSynonyms){
254
                    resultObjects.add( new UuidAndTitleCache<>(Synonym.class, (UUID) result[0], (Integer) result[1], (String)result[2], new Boolean(result[4].toString()), null));
255
                }
256
            }
257
        }
258
        return resultObjects;
259

    
260
    }
261

    
262
    @Override
263
    public List<Taxon> getTaxaByCommonName(String queryString, Classification classification,
264
               MatchMode matchMode, Set<NamedArea> namedAreas, Integer pageSize,
265
               Integer pageNumber, List<String> propertyPaths) {
266
        boolean doCount = false;
267
        Query query = prepareTaxaByCommonName(queryString, classification, matchMode, namedAreas, pageSize, pageNumber, doCount, false);
268
        if (query != null){
269
            @SuppressWarnings("unchecked")
270
            List<Taxon> results = query.list();
271
            defaultBeanInitializer.initializeAll(results, propertyPaths);
272
            return results;
273
        }else{
274
            return new ArrayList<>();
275
        }
276

    
277
    }
278

    
279
    /**
280
     * @param clazz
281
     * @param searchField the field in TaxonName to be searched through usually either <code>nameCache</code> or <code>titleCache</code>
282
     * @param queryString
283
     * @param classification TODO
284
     * @param matchMode
285
     * @param namedAreas
286
     * @param pageSize
287
     * @param pageNumber
288
     * @param doCount
289
     * @return
290
     *
291
     *
292
     */
293
    private Query prepareTaxaByNameForEditor(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, boolean doCommonNames,
294
            boolean includeUnpublished, String searchField, String queryString, Classification classification,
295
            MatchMode matchMode, Set<NamedArea> namedAreas, boolean doCount, NameSearchOrder order) {
296
        return prepareByNameQuery(doTaxa, doSynonyms, doMisappliedNames, doCommonNames, includeUnpublished,
297
                searchField, queryString,
298
                classification, matchMode, namedAreas, order, doCount, true);
299
    }
300

    
301

    
302
    /**
303
     * @param doTaxa
304
     * @param doSynonyms
305
     * @param doIncludeMisappliedNames
306
     * @param doCommonNames
307
     * @param includeUnpublished
308
     * @param searchField
309
     * @param queryString
310
     * @param classification
311
     * @param matchMode
312
     * @param namedAreas
313
     * @param order
314
     * @param doCount
315
     * @param returnIdAndTitle
316
     *            If set true the seach method will not return synonym and taxon
317
     *            entities but an array containing the uuid, titleCache, and the
318
     *            DTYPE in lowercase letters.
319
     * @return
320
     */
321
    private Query prepareByNameQuery(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames,
322
                boolean doCommonNames, boolean includeUnpublished, String searchField, String queryString,
323
                Classification classification, MatchMode matchMode, Set<NamedArea> namedAreas,
324
                NameSearchOrder order, boolean doCount, boolean returnIdAndTitle){
325

    
326
            boolean doProParteSynonyms = doSynonyms;  //we may distinguish in future
327
            boolean doConceptRelations = doMisappliedNames || doProParteSynonyms;
328

    
329
            if (order == null){
330
                order = NameSearchOrder.DEFAULT();
331
            }
332
            String hqlQueryString = matchMode.queryStringFrom(queryString);
333
            String selectWhat;
334
            if (returnIdAndTitle){
335
                selectWhat = "t.uuid, t.id, t.titleCache ";
336
            }else {
337
                selectWhat = (doCount ? "count(t)": "t");
338
            }
339

    
340
            //area filter
341
            //TODO share code with taxon node filter
342
            String hql = "";
343
            Set<NamedArea> areasExpanded = new HashSet<>();
344
            if(namedAreas != null && namedAreas.size() > 0){
345
                // expand areas and restrict by distribution area
346
                Query areaQuery = getSession().createQuery("SELECT childArea "
347
                        + " FROM NamedArea AS childArea LEFT JOIN childArea.partOf as parentArea "
348
                        + " WHERE parentArea = :area");
349
                expandNamedAreas(namedAreas, areasExpanded, areaQuery);
350
            }
351
            boolean doAreaRestriction = areasExpanded.size() > 0;
352

    
353
            Set<UUID> namedAreasUuids = new HashSet<>();
354
            for (NamedArea area:areasExpanded){
355
                namedAreasUuids.add(area.getUuid());
356
            }
357

    
358
            Subselects subSelects = createByNameHQLString(doConceptRelations,
359
                    includeUnpublished, classification, areasExpanded, matchMode, searchField);
360
            String taxonSubselect = subSelects.taxonSubselect;
361
            String synonymSubselect = subSelects.synonymSubselect;
362
            String conceptSelect = subSelects.conceptSelect;
363
            String commonNameSubSelect = subSelects.commonNameSubselect;
364

    
365

    
366
            if (logger.isDebugEnabled()) {
367
                logger.debug("taxonSubselect: " + (taxonSubselect != null ? taxonSubselect: "NULL"));
368
                logger.debug("synonymSubselect: " + (synonymSubselect != null ? synonymSubselect: "NULL"));
369
            }
370

    
371
            List<Integer> taxonIDs = new ArrayList<>();
372
            List<Integer> synonymIDs = new ArrayList<>();
373

    
374
            if(doTaxa){
375
                // find Taxa
376
                Query subTaxon = getSearchQueryString(hqlQueryString, taxonSubselect);
377

    
378
                addRestrictions(doAreaRestriction, classification, includeUnpublished,
379
                        namedAreasUuids, subTaxon);
380
                taxonIDs = subTaxon.list();
381
            }
382

    
383
            if(doSynonyms){
384
                // find synonyms
385
                Query subSynonym = getSearchQueryString(hqlQueryString, synonymSubselect);
386
                addRestrictions(doAreaRestriction, classification, includeUnpublished, namedAreasUuids,subSynonym);
387
                synonymIDs = subSynonym.list();
388
            }
389
            if (doConceptRelations ){
390
                Query subMisappliedNames = getSearchQueryString(hqlQueryString, conceptSelect);
391
                Set<TaxonRelationshipType> relTypeSet = new HashSet<>();
392
                if (doMisappliedNames){
393
                    relTypeSet.addAll(TaxonRelationshipType.allMisappliedNameTypes());
394
                }
395
                if (doProParteSynonyms){
396
                    relTypeSet.addAll(TaxonRelationshipType.allSynonymTypes());
397
                }
398
                subMisappliedNames.setParameterList("rTypeSet", relTypeSet);
399
                addRestrictions(doAreaRestriction, classification, includeUnpublished, namedAreasUuids, subMisappliedNames);
400
                taxonIDs.addAll(subMisappliedNames.list());
401
            }
402

    
403
            if(doCommonNames){
404
                // find Taxa
405
                Query subCommonNames = getSearchQueryString(hqlQueryString, commonNameSubSelect);
406
                addRestrictions(doAreaRestriction, classification, includeUnpublished, namedAreasUuids, subCommonNames);
407
                taxonIDs.addAll(subCommonNames.list());
408
            }
409

    
410

    
411
            if(synonymIDs.size()>0 && taxonIDs.size()>0){
412
                hql = "SELECT " + selectWhat;
413
                // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
414
                // also return the computed isOrphaned flag
415
                if (returnIdAndTitle &&  !doCount ){
416
                    hql += ", CASE WHEN t.id in (:taxa) THEN 'taxon' ELSE 'synonym' END, " +
417
                            " CASE WHEN t.id in (:taxa) "
418
                                    + " AND t.taxonNodes IS EMPTY "
419
                                    + " AND t.relationsFromThisTaxon IS EMPTY "
420
                                    + " AND t.relationsToThisTaxon IS EMPTY "
421
                                 + " THEN true ELSE false END ";
422
                }
423
                hql +=  " FROM %s t " +
424
                        " WHERE (t.id in (:taxa) OR t.id IN (:synonyms)) ";
425
            }else if (synonymIDs.size()>0 ){
426
                hql = "SELECT " + selectWhat;
427
                // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
428
                // also return the computed isOrphaned flag
429
                if (returnIdAndTitle &&  !doCount ){
430
                    hql += ", 'synonym', 'false' ";
431

    
432
                }
433
                hql +=  " FROM %s t " +
434
                        " WHERE t.id in (:synonyms) ";
435

    
436
            } else if (taxonIDs.size()>0 ){
437
                hql = "SELECT " + selectWhat;
438
                // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
439
                // also return the computed isOrphaned flag
440
                if (returnIdAndTitle &&  !doCount ){
441
                    hql += ", 'taxon', " +
442
                            " CASE WHEN t.taxonNodes is empty "
443
                            + "  AND t.relationsFromThisTaxon is empty "
444
                            + "  AND t.relationsToThisTaxon is empty "
445
                            + "THEN true ELSE false END ";
446
                }
447
                hql +=  " FROM %s t " +
448
                        " WHERE t.id in (:taxa) ";
449
            } else if (StringUtils.isBlank(queryString)){
450
                hql = "SELECT " + selectWhat + " FROM %s t";
451
            } else{
452
                return null;
453
            }
454

    
455
            String classString;
456
            if ((doTaxa || doCommonNames || doConceptRelations) && doSynonyms){
457
                classString = "TaxonBase";
458
            } else if (doTaxa || doCommonNames){
459
                classString = "Taxon";
460
            } else if (doSynonyms && !(doCommonNames|| doTaxa || doConceptRelations)){
461
                classString = "Synonym";  // as long as doProParteSynonyms = doSynonyms this case should not happen
462
            } else{//only misappliedNames
463
                classString = "Taxon";
464
            }
465

    
466
            hql = String.format(hql, classString);
467

    
468
            if (hql == "") {
469
                return null;
470
            }
471
            if(!doCount){
472
                String orderBy = " ORDER BY ";
473
                String alphabeticBase = " t.name.genusOrUninomial, case when t.name.specificEpithet like '\"%\"' then 1 else 0 end, t.name.specificEpithet, t.name.rank desc, t.name.nameCache";
474

    
475
                if (order == NameSearchOrder.LENGTH_ALPHA_NAME){
476
                    orderBy += " length(t.name.nameCache), " + alphabeticBase;
477
                }else if (order == NameSearchOrder.LENGTH_ALPHA_TITLE){
478
                    orderBy += " length(t.name.titleCache), " + alphabeticBase;
479
                }else {
480
                    orderBy += alphabeticBase;
481
                }
482

    
483
                hql += orderBy;
484
            }
485

    
486
            if(logger.isDebugEnabled()){ logger.debug("hql: " + hql);}
487
            Query query = getSession().createQuery(hql);
488

    
489
            // find taxa and synonyms
490
            if (taxonIDs.size()>0){
491
                query.setParameterList("taxa", taxonIDs);
492
            }
493
            if (synonymIDs.size()>0){
494
                query.setParameterList("synonyms",synonymIDs);
495
            }
496
            if (taxonIDs.size()== 0 && synonymIDs.size() == 0){
497
                return null;
498
            }
499

    
500
            return query;
501
    }
502

    
503
    /**
504
     * @param hqlQueryString
505
     * @param synonymSubselect
506
     * @return
507
     */
508
    protected Query getSearchQueryString(String hqlQueryString, String synonymSubselect) {
509
        return getSession().createQuery(synonymSubselect).setParameter("queryString", hqlQueryString);
510
    }
511

    
512
    /**
513
     * @param includeUnpublished
514
     * @param classification
515
     * @param doAreaRestriction
516
     * @param namedAreasUuids
517
     * @param subTaxon
518
     */
519
    protected void addRestrictions(boolean doAreaRestriction, Classification classification, boolean includeUnpublished,
520
            Set<UUID> namedAreasUuids, Query query) {
521
        if(doAreaRestriction){
522
            query.setParameterList("namedAreasUuids", namedAreasUuids);
523
        }
524
        if(classification != null){
525
            query.setParameter("classification", classification);
526
        }
527
        if(!includeUnpublished){
528
            query.setBoolean("publish", true);
529
        }
530
    }
531

    
532

    
533
    /**
534
     * @param searchField the field in TaxonName to be searched through usually either <code>nameCache</code> or <code>titleCache</code>
535
     * @param queryString
536
     * @param classification TODO
537
     * @param matchMode
538
     * @param namedAreas
539
     * @param pageSize
540
     * @param pageNumber
541
     * @param doCount
542
     * @param clazz
543
     * @return
544
     *
545
     * FIXME implement classification restriction & implement test: see {@link TaxonDaoHibernateImplTest#testCountTaxaByName()}
546
     */
547
    private Query prepareTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames,
548
            boolean doCommonNames, boolean includeUnpublished, String searchField, String queryString,
549
            Classification classification, MatchMode matchMode, Set<NamedArea> namedAreas, NameSearchOrder order, Integer pageSize, Integer pageNumber, boolean doCount) {
550

    
551
        Query query = prepareByNameQuery(doTaxa, doSynonyms, doMisappliedNames, doCommonNames, includeUnpublished,
552
                searchField, queryString, classification, matchMode, namedAreas, order, doCount, false);
553

    
554
        if(pageSize != null && !doCount && query != null) {
555
            query.setMaxResults(pageSize);
556
            if(pageNumber != null) {
557
                query.setFirstResult(pageNumber * pageSize);
558
            }
559
        }
560

    
561
        return query;
562
    }
563

    
564
    private Query prepareTaxaByCommonName(String queryString, Classification classification,
565
            MatchMode matchMode, Set<NamedArea> namedAreas, Integer pageSize, Integer pageNumber,
566
            boolean doCount, boolean returnIdAndTitle){
567

    
568
        String what = "select distinct";
569
        if (returnIdAndTitle){
570
        	what += " t.uuid, t.id, t.titleCache, \'taxon\', case when t.taxonNodes is empty and t.relationsFromThisTaxon is empty and t.relationsToThisTaxon is empty then true else false end ";
571
        }else {
572
        	what += (doCount ? " count(t)": " t");
573
        }
574
        String hql= what + " from Taxon t " +
575
        "join t.descriptions d "+
576
        "join d.descriptionElements e " +
577
        "join e.feature f " +
578
        "where f.supportsCommonTaxonName = true and e.name "+matchMode.getMatchOperator()+" :queryString";//and ls.text like 'common%'";
579

    
580
        Query query = getSession().createQuery(hql);
581

    
582
        query.setParameter("queryString", matchMode.queryStringFrom(queryString));
583

    
584
        if(pageSize != null &&  !doCount) {
585
            query.setMaxResults(pageSize);
586
            if(pageNumber != null) {
587
                query.setFirstResult(pageNumber * pageSize);
588
            }
589
        }
590
        return query;
591
    }
592

    
593
    @Override
594
    public long countTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, boolean doCommonNames,
595
            boolean doIncludeAuthors, String queryString, Classification classification,
596
        MatchMode matchMode, Set<NamedArea> namedAreas, boolean includeUnpublished) {
597

    
598
        boolean doCount = true;
599
        /*
600
        boolean doTaxa = true;
601
        boolean doSynonyms = true;
602
        if (clazz.equals(Taxon.class)){
603
            doSynonyms = false;
604
        } else if (clazz.equals(Synonym.class)){
605
            doTaxa = false;
606
        }
607
        */
608
        String searchField = doIncludeAuthors ? "titleCache": "nameCache";
609

    
610
        Query query = prepareTaxaByName(doTaxa, doSynonyms, doMisappliedNames, doCommonNames, includeUnpublished,
611
                searchField, queryString, classification, matchMode, namedAreas, null, null, null, doCount);
612
        if (query != null) {
613
            return (Long)query.uniqueResult();
614
        }else{
615
            return 0;
616
        }
617
    }
618

    
619
    /**
620
     * @param namedAreas
621
     * @param areasExpanded
622
     * @param areaQuery
623
     */
624
    private void expandNamedAreas(Collection<NamedArea> namedAreas, Set<NamedArea> areasExpanded, Query areaQuery) {
625
        List<NamedArea> childAreas;
626
        for(NamedArea a : namedAreas){
627
            areasExpanded.add(a);
628
            areaQuery.setParameter("area", a);
629
            childAreas = areaQuery.list();
630
            if(childAreas.size() > 0){
631
                areasExpanded.addAll(childAreas);
632
                expandNamedAreas(childAreas, areasExpanded, areaQuery);
633
            }
634
        }
635
    }
636

    
637

    
638
    @Override
639
    public UUID delete(TaxonBase taxonBase) throws DataAccessException{
640
        if (taxonBase == null){
641
            logger.warn("TaxonBase was 'null'");
642
            return null;
643
        }
644

    
645
        // Merge the object in if it is detached
646
        //
647
        // I think this is preferable to catching lazy initialization errors
648
        // as that solution only swallows and hides the exception, but doesn't
649
        // actually solve it.
650
        taxonBase = (TaxonBase)getSession().merge(taxonBase);
651

    
652
        taxonBase.removeSources();
653

    
654

    
655
        if (taxonBase instanceof Taxon){ // is Taxon
656
            Taxon taxon = ((Taxon)taxonBase);
657
            Set<Synonym> syns = new HashSet<>(taxon.getSynonyms());
658
            for (Synonym syn: syns){
659
                taxon.removeSynonym(syn);
660
            }
661
        }
662

    
663
       return super.delete(taxonBase);
664

    
665
    }
666

    
667
    @Override
668
    public List<TaxonBase> findByNameTitleCache(boolean doTaxa, boolean doSynonyms, boolean includeUnpublished, String queryString, Classification classification, MatchMode matchMode, Set<NamedArea> namedAreas, NameSearchOrder order, Integer pageNumber, Integer pageSize, List<String> propertyPaths) {
669

    
670
        boolean doCount = false;
671
        Query query = prepareTaxaByName(doTaxa, doSynonyms, false, false, includeUnpublished, "titleCache", queryString, classification, matchMode, namedAreas, order, pageSize, pageNumber, doCount);
672
        if (query != null){
673
            @SuppressWarnings({ "unchecked", "rawtypes" })
674
            List<TaxonBase> results = query.list();
675
            defaultBeanInitializer.initializeAll(results, propertyPaths);
676
            return results;
677
        }
678
        return new ArrayList<>();
679

    
680
    }
681

    
682
    @Override
683
    public TaxonBase findByUuid(UUID uuid, List<Criterion> criteria, List<String> propertyPaths) {
684

    
685
        Criteria crit = getSession().createCriteria(type);
686

    
687
        if (uuid != null) {
688
            crit.add(Restrictions.eq("uuid", uuid));
689
        } else {
690
            logger.warn("UUID is NULL");
691
            return null;
692
        }
693
        if(criteria != null){
694
            for (Criterion criterion : criteria) {
695
                crit.add(criterion);
696
            }
697
        }
698
        crit.addOrder(Order.asc("uuid"));
699

    
700
        @SuppressWarnings({ "unchecked", "rawtypes" })
701
        List<? extends TaxonBase> results = crit.list();
702
        if (results.size() == 1) {
703
            defaultBeanInitializer.initializeAll(results, propertyPaths);
704
            TaxonBase<?> taxon = results.iterator().next();
705
            return taxon;
706
        } else if (results.size() > 1) {
707
            logger.error("Multiple results for UUID: " + uuid);
708
        } else if (results.size() == 0) {
709
            logger.info("No results for UUID: " + uuid);
710
        }
711

    
712
        return null;
713
    }
714

    
715
    @Override
716
    public List<? extends TaxonBase> findByUuids(List<UUID> uuids, List<Criterion> criteria, List<String> propertyPaths) {
717

    
718
        Criteria crit = getSession().createCriteria(type);
719

    
720
        if (uuids != null) {
721
            crit.add(Restrictions.in("uuid", uuids));
722
        } else {
723
            logger.warn("List<UUID> uuids is NULL");
724
            return null;
725
        }
726
        if(criteria != null){
727
            for (Criterion criterion : criteria) {
728
                crit.add(criterion);
729
            }
730
        }
731
        crit.addOrder(Order.asc("uuid"));
732

    
733
        @SuppressWarnings({ "unchecked", "rawtypes" })
734
        List<? extends TaxonBase> results = crit.list();
735

    
736
        defaultBeanInitializer.initializeAll(results, propertyPaths);
737
        return results;
738
    }
739

    
740
    @Override
741
    public long countMatchesByName(String queryString, MatchMode matchMode, boolean onlyAcccepted) {
742
        checkNotInPriorView("TaxonDaoHibernateImpl.countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted)");
743

    
744
        Criteria crit = getCriteria(type);
745
        crit.add(Restrictions.ilike("titleCache", matchMode.queryStringFrom(queryString)));
746
        crit.setProjection(Projections.rowCount());
747
        return (Long)crit.uniqueResult();
748
    }
749

    
750

    
751
    @Override
752
    public long countMatchesByName(String queryString, MatchMode matchMode, boolean onlyAcccepted, List<Criterion> criteria) {
753
        checkNotInPriorView("TaxonDaoHibernateImpl.countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted, List<Criterion> criteria)");
754

    
755
        Criteria crit = getCriteria(type);
756
        crit.add(Restrictions.ilike("titleCache", matchMode.queryStringFrom(queryString)));
757
        if(criteria != null){
758
            for (Criterion criterion : criteria) {
759
                crit.add(criterion);
760
            }
761
        }
762
        crit.setProjection(Projections.rowCount());
763
        return (Long)crit.uniqueResult();
764
    }
765

    
766

    
767
    @Override
768
    public long countSynonyms(boolean onlyAttachedToTaxon) {
769
        AuditEvent auditEvent = getAuditEventFromContext();
770
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
771
            Query query = null;
772

    
773
            String queryStr =
774
                    " SELECT count(syn) "
775
                  + " FROM Synonym syn";
776
            if (onlyAttachedToTaxon){
777
                queryStr += " WHERE syn.acceptedTaxon IS NOT NULL";
778
            }
779
            query = getSession().createQuery(queryStr);
780

    
781
            return (Long)query.uniqueResult();
782
        } else {
783
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(Synonym.class,auditEvent.getRevisionNumber());
784
            if (onlyAttachedToTaxon){
785
                query.add(new NotNullAuditExpression(new EntityPropertyName("acceptedTaxon")));
786
            }
787
            query.addProjection(AuditEntity.id().count());
788

    
789
            return (Long)query.getSingleResult();
790
        }
791
    }
792

    
793
    @Override
794
    public long countSynonyms(Taxon taxon, SynonymType type) {
795
        AuditEvent auditEvent = getAuditEventFromContext();
796
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
797
            Criteria criteria = getCriteria(Synonym.class);
798

    
799
            criteria.add(Restrictions.eq("acceptedTaxon", taxon));
800
            if(type != null) {
801
                criteria.add(Restrictions.eq("type", type));
802
            }
803
            criteria.setProjection(Projections.rowCount());
804
            return (Long)criteria.uniqueResult();
805
        } else {
806
            AuditQuery query = makeAuditQuery(Synonym.class,auditEvent);
807
            query.add(AuditEntity.relatedId("acceptedTaxon").eq(taxon.getId()));
808
            query.addProjection(AuditEntity.id().count());
809

    
810
            if(type != null) {
811
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
812
            }
813

    
814
            return (Long)query.getSingleResult();
815
        }
816
    }
817

    
818
    @Override
819
    public long countSynonyms(Synonym synonym, SynonymType type) {
820
        AuditEvent auditEvent = getAuditEventFromContext();
821
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
822
            Criteria criteria = getCriteria(Synonym.class);
823

    
824
            criteria.add(Restrictions.isNotNull("acceptedTaxon"));
825
            if(type != null) {
826
                criteria.add(Restrictions.eq("type", type));
827
            }
828

    
829
            criteria.setProjection(Projections.rowCount());
830
            return (Long)criteria.uniqueResult();
831
        } else {
832
            AuditQuery query = makeAuditQuery(Synonym.class,auditEvent);
833
            query.add(new NotNullAuditExpression(new EntityPropertyName("acceptedTaxon")));
834
            query.addProjection(AuditEntity.id().count());
835

    
836
            if(type != null) {
837
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
838
            }
839

    
840
            return (Long)query.getSingleResult();
841
        }
842
    }
843

    
844
    @Override
845
    public long countTaxaByName(Class<? extends TaxonBase> clazz, String genusOrUninomial, String infraGenericEpithet, String specificEpithet,	String infraSpecificEpithet, Rank rank) {
846
        checkNotInPriorView("TaxonDaoHibernateImpl.countTaxaByName(Boolean accepted, String genusOrUninomial,	String infraGenericEpithet, String specificEpithet,	String infraSpecificEpithet, Rank rank)");
847
        Criteria criteria = null;
848

    
849
        criteria = getCriteria(clazz);
850

    
851
        criteria.setFetchMode( "name", FetchMode.JOIN );
852
        criteria.createAlias("name", "name");
853

    
854
        if(genusOrUninomial == null) {
855
            criteria.add(Restrictions.isNull("name.genusOrUninomial"));
856
        } else if(!genusOrUninomial.equals("*")) {
857
            criteria.add(Restrictions.eq("name.genusOrUninomial", genusOrUninomial));
858
        }
859

    
860
        if(infraGenericEpithet == null) {
861
            criteria.add(Restrictions.isNull("name.infraGenericEpithet"));
862
        } else if(!infraGenericEpithet.equals("*")) {
863
            criteria.add(Restrictions.eq("name.infraGenericEpithet", infraGenericEpithet));
864
        }
865

    
866
        if(specificEpithet == null) {
867
            criteria.add(Restrictions.isNull("name.specificEpithet"));
868
        } else if(!specificEpithet.equals("*")) {
869
            criteria.add(Restrictions.eq("name.specificEpithet", specificEpithet));
870

    
871
        }
872

    
873
        if(infraSpecificEpithet == null) {
874
            criteria.add(Restrictions.isNull("name.infraSpecificEpithet"));
875
        } else if(!infraSpecificEpithet.equals("*")) {
876
            criteria.add(Restrictions.eq("name.infraSpecificEpithet", infraSpecificEpithet));
877
        }
878

    
879
        if(rank != null) {
880
            criteria.add(Restrictions.eq("name.rank", rank));
881
        }
882

    
883
        criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
884

    
885
        return (Long)criteria.uniqueResult();
886
    }
887

    
888
    @Override
889
    public List<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, String authorship, Rank rank, Integer pageSize,	Integer pageNumber) {
890
        checkNotInPriorView("TaxonDaoHibernateImpl.findTaxaByName(Boolean accepted, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, String authorship, Rank rank, Integer pageSize,	Integer pageNumber)");
891
        Criteria criteria = getCriteria(clazz);
892

    
893
        criteria.setFetchMode( "name", FetchMode.JOIN );
894
        criteria.createAlias("name", "name");
895

    
896
        if(genusOrUninomial == null) {
897
            criteria.add(Restrictions.isNull("name.genusOrUninomial"));
898
        } else if(!genusOrUninomial.equals("*")) {
899
            criteria.add(Restrictions.eq("name.genusOrUninomial", genusOrUninomial));
900
        }
901

    
902
        if(infraGenericEpithet == null) {
903
            criteria.add(Restrictions.isNull("name.infraGenericEpithet"));
904
        } else if(!infraGenericEpithet.equals("*")) {
905
            criteria.add(Restrictions.eq("name.infraGenericEpithet", infraGenericEpithet));
906
        }
907

    
908
        if(specificEpithet == null) {
909
            criteria.add(Restrictions.isNull("name.specificEpithet"));
910
        } else if(!specificEpithet.equals("*")) {
911
            criteria.add(Restrictions.eq("name.specificEpithet", specificEpithet));
912

    
913
        }
914

    
915
        if(infraSpecificEpithet == null) {
916
            criteria.add(Restrictions.isNull("name.infraSpecificEpithet"));
917
        } else if(!infraSpecificEpithet.equals("*")) {
918
            criteria.add(Restrictions.eq("name.infraSpecificEpithet", infraSpecificEpithet));
919
        }
920

    
921
        if(authorship == null) {
922
            criteria.add(Restrictions.eq("name.authorshipCache", ""));
923
        } else if(!authorship.equals("*")) {
924
            criteria.add(Restrictions.eq("name.authorshipCache", authorship));
925
        }
926

    
927
        if(rank != null) {
928
            criteria.add(Restrictions.eq("name.rank", rank));
929
        }
930

    
931
        if(pageSize != null) {
932
            criteria.setMaxResults(pageSize);
933
            if(pageNumber != null) {
934
                criteria.setFirstResult(pageNumber * pageSize);
935
            } else {
936
                criteria.setFirstResult(0);
937
            }
938
        }
939

    
940
        @SuppressWarnings({ "unchecked", "rawtypes" })
941
        List<TaxonBase> result = criteria.list();
942
        return result;
943
    }
944

    
945

    
946
    @Override
947
    public long countTaxonRelationships(Taxon taxon, TaxonRelationshipType type,
948
            boolean includeUnpublished, Direction direction) {
949
        AuditEvent auditEvent = getAuditEventFromContext();
950
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
951

    
952
            String queryString = prepareTaxonRelationshipQuery(type, includeUnpublished, direction, true);
953
            Query query = getSession().createQuery(queryString);
954
            query.setParameter("relatedTaxon", taxon);
955
            if(type != null) {
956
                query.setParameter("type",type);
957
            }
958
            if(! includeUnpublished) {
959
                query.setBoolean("publish",Boolean.TRUE);
960
            }
961

    
962

    
963
//            if(type == null) {
964
//                query = getSession().createQuery(
965
//                        "select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon");
966
//            } else {
967
//                query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon and taxonRelationship.type = :type");
968
//                query.setParameter("type",type);
969
//            }
970
//            query.setParameter("relatedTaxon", taxon);
971

    
972
            return (Long)query.uniqueResult();
973
        } else {
974
          //TODO unpublished
975

    
976
            AuditQuery query = makeAuditQuery(TaxonRelationship.class, auditEvent);
977
            query.add(AuditEntity.relatedId(direction.toString()).eq(taxon.getId()));
978
            query.addProjection(AuditEntity.id().count());
979

    
980
            if(type != null) {
981
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
982
            }
983

    
984
            return (Long)query.getSingleResult();
985
        }
986
    }
987

    
988

    
989
    /**
990
     * @param type
991
     * @param includeUnpublished
992
     * @param direction
993
     * @param b
994
     * @return
995
     */
996
    private String prepareTaxonRelationshipQuery(TaxonRelationshipType type, boolean includeUnpublished,
997
            Direction direction, boolean isCount) {
998
        String selectStr = isCount? " count(rel) as n ":" rel ";
999
        String result = "SELECT " + selectStr +
1000
             " FROM TaxonRelationship rel " +
1001
             " WHERE rel."+direction+" = :relatedTaxon";
1002
        if (type != null){
1003
            result += " AND rel.type = :type ";
1004
        }
1005
        if(! includeUnpublished) {
1006
            result += " AND rel."+direction.invers()+".publish = :publish";
1007
        }
1008
        return result;
1009
    }
1010

    
1011
    @Override
1012
    public List<TaxonRelationship> getTaxonRelationships(Taxon taxon, TaxonRelationshipType type,
1013
            boolean includeUnpublished, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
1014
            List<String> propertyPaths, Direction direction) {
1015

    
1016
        AuditEvent auditEvent = getAuditEventFromContext();
1017
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1018

    
1019
            String queryString = prepareTaxonRelationshipQuery(type, includeUnpublished, direction, false);
1020

    
1021
            queryString += orderByClause("rel", orderHints);
1022

    
1023
            Query query = getSession().createQuery(queryString);
1024
            query.setParameter("relatedTaxon", taxon);
1025
            if(type != null) {
1026
                query.setParameter("type",type);
1027
            }
1028
            if(! includeUnpublished) {
1029
                query.setBoolean("publish",Boolean.TRUE);
1030
            }
1031
            setPagingParameter(query, pageSize, pageNumber);
1032

    
1033
//            Criteria criteria = getSession().createCriteria(TaxonRelationship.class);
1034
//
1035
//            if(direction != null) {
1036
//                criteria.add(Restrictions.eq(direction.name(), taxon));
1037
//            } else {
1038
//                criteria.add(Restrictions.or(
1039
//                        Restrictions.eq(Direction.relatedFrom.name(), taxon),
1040
//                        Restrictions.eq(Direction.relatedTo.name(), taxon))
1041
//                    );
1042
//            }
1043
//
1044
//            if(type != null) {
1045
//                criteria.add(Restrictions.eq("type", type));
1046
//            }
1047
//
1048
//            addOrder(criteria,orderHints);
1049
//
1050
//            if(pageSize != null) {
1051
//                criteria.setMaxResults(pageSize);
1052
//                if(pageNumber != null) {
1053
//                    criteria.setFirstResult(pageNumber * pageSize);
1054
//                } else {
1055
//                    criteria.setFirstResult(0);
1056
//                }
1057
//            }
1058

    
1059
            @SuppressWarnings("unchecked")
1060
            List<TaxonRelationship> result = query.list();
1061
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1062

    
1063
            return result;
1064
        } else {
1065
            //TODO unpublished
1066
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1067
            query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));
1068

    
1069
            if(type != null) {
1070
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1071
            }
1072

    
1073
            if(pageSize != null) {
1074
                query.setMaxResults(pageSize);
1075
                if(pageNumber != null) {
1076
                    query.setFirstResult(pageNumber * pageSize);
1077
                } else {
1078
                    query.setFirstResult(0);
1079
                }
1080
            }
1081

    
1082
            @SuppressWarnings("unchecked")
1083
            List<TaxonRelationship> result = query.getResultList();
1084
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1085

    
1086
            // Ugly, but for now, there is no way to sort on a related entity property in Envers,
1087
            // and we can't live without this functionality in CATE as it screws up the whole
1088
            // taxon tree thing
1089
            if(orderHints != null && !orderHints.isEmpty()) {
1090
                SortedSet<TaxonRelationship> sortedList = new TreeSet<>(new TaxonRelationshipFromTaxonComparator());
1091
                sortedList.addAll(result);
1092
                return new ArrayList<>(sortedList);
1093
            }
1094

    
1095
            return result;
1096
        }
1097
    }
1098

    
1099
    class TaxonRelationshipFromTaxonComparator implements Comparator<TaxonRelationship> {
1100

    
1101
        @Override
1102
        public int compare(TaxonRelationship o1, TaxonRelationship o2) {
1103
            if (o1.equals(o2)){
1104
                return 0;
1105
            }
1106
            int result = o1.getFromTaxon().getTitleCache().compareTo(o2.getFromTaxon().getTitleCache());
1107
            if (result == 0 ){
1108
                result = o1.getUuid().compareTo(o2.getUuid());
1109
            }
1110
            return result;
1111
        }
1112

    
1113
    }
1114

    
1115
    @Override
1116
    public List<Synonym> getSynonyms(Taxon taxon, SynonymType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
1117
        AuditEvent auditEvent = getAuditEventFromContext();
1118
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1119
            Criteria criteria = getSession().createCriteria(Synonym.class);
1120

    
1121
            criteria.add(Restrictions.eq("acceptedTaxon", taxon));
1122
            if(type != null) {
1123
                criteria.add(Restrictions.eq("type", type));
1124
            }
1125

    
1126
            addOrder(criteria,orderHints);
1127

    
1128
            if(pageSize != null) {
1129
                criteria.setMaxResults(pageSize);
1130
                if(pageNumber != null) {
1131
                    criteria.setFirstResult(pageNumber * pageSize);
1132
                } else {
1133
                    criteria.setFirstResult(0);
1134
                }
1135
            }
1136

    
1137
            @SuppressWarnings("unchecked")
1138
            List<Synonym> result = criteria.list();
1139
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1140

    
1141
            return result;
1142
        } else {
1143
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(Synonym.class,auditEvent.getRevisionNumber());
1144
            query.add(AuditEntity.relatedId("acceptedTaxon").eq(taxon.getId()));
1145

    
1146
            if(type != null) {
1147
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1148
            }
1149

    
1150
            if(pageSize != null) {
1151
                query.setMaxResults(pageSize);
1152
                if(pageNumber != null) {
1153
                    query.setFirstResult(pageNumber * pageSize);
1154
                } else {
1155
                    query.setFirstResult(0);
1156
                }
1157
            }
1158

    
1159
            @SuppressWarnings("unchecked")
1160
            List<Synonym> result = query.getResultList();
1161
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1162

    
1163
            return result;
1164
        }
1165
    }
1166

    
1167
    @Override
1168
    public void rebuildIndex() {
1169
        FullTextSession fullTextSession = Search.getFullTextSession(getSession());
1170

    
1171
        for(TaxonBase<?> taxonBase : list(null,null)) { // re-index all taxon base
1172
            Hibernate.initialize(taxonBase.getName());
1173
            fullTextSession.index(taxonBase);
1174
        }
1175
        fullTextSession.flushToIndexes();
1176
    }
1177

    
1178
    @Override
1179
    public String suggestQuery(String queryString) {
1180
        throw new RuntimeException("Query suggestion currently not implemented in TaxonDaoHibernateImpl");
1181
//        checkNotInPriorView("TaxonDaoHibernateImpl.suggestQuery(String queryString)");
1182
//        String alternativeQueryString = null;
1183
//        if (alternativeSpellingSuggestionParser != null) {
1184
//            try {
1185
//
1186
//                alternativeSpellingSuggestionParser.parse(queryString);
1187
//                org.apache.lucene.search.Query alternativeQuery = alternativeSpellingSuggestionParser.suggest(queryString);
1188
//                if (alternativeQuery != null) {
1189
//                    alternativeQueryString = alternativeQuery
1190
//                            .toString("name.titleCache");
1191
//                }
1192
//
1193
//            } catch (ParseException e) {
1194
//                throw new QueryParseException(e, queryString);
1195
//            }
1196
//        }
1197
//        return alternativeQueryString;
1198
    }
1199

    
1200
    @Override
1201
    public Taxon acceptedTaxonFor(Synonym synonym, Classification classificationFilter, List<String> propertyPaths){
1202

    
1203
        String hql = prepareListAcceptedTaxaFor(classificationFilter, false);
1204

    
1205
        Query query = getSession().createQuery(hql);
1206
        query.setParameter("synonym", synonym);
1207
        if(classificationFilter != null){
1208
            query.setParameter("classificationFilter", classificationFilter);
1209
        }
1210

    
1211
        @SuppressWarnings("unchecked")
1212
        List<Taxon> result = query.list();
1213

    
1214
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1215

    
1216
        return result.isEmpty()? null: result.get(0);
1217
    }
1218

    
1219
    @Override
1220
    public long countAcceptedTaxonFor(Synonym synonym, Classification classificationFilter){
1221

    
1222
        String hql = prepareListAcceptedTaxaFor(classificationFilter, true);
1223

    
1224
        Query query = getSession().createQuery(hql);
1225
        query.setParameter("synonym", synonym);
1226
        if(classificationFilter != null){
1227
            query.setParameter("classificationFilter", classificationFilter);
1228
        }
1229

    
1230
        Long count = Long.parseLong(query.uniqueResult().toString());
1231
        return count;
1232

    
1233
    }
1234

    
1235

    
1236
    /**
1237
     * @param classificationFilter
1238
     * @param orderHints
1239
     * @return
1240
     */
1241
    private String prepareListAcceptedTaxaFor(Classification classificationFilter, boolean doCount) {
1242

    
1243
        String hql;
1244
        String hqlSelect = "SELECT " + (doCount? "COUNT(taxon)" : "taxon") +
1245
                 " FROM Synonym as syn "
1246
                 + "   JOIN syn.acceptedTaxon as taxon ";
1247
        String hqlWhere = " WHERE syn = :synonym";
1248

    
1249
        if(classificationFilter != null){
1250
            hqlSelect += " JOIN taxon.taxonNodes AS taxonNode";
1251
            hqlWhere  += " AND taxonNode.classification = :classificationFilter";
1252
        }
1253
        hql = hqlSelect + hqlWhere;
1254
        return hql;
1255
    }
1256

    
1257
    @Override
1258
    public TaxonBase find(LSID lsid) {
1259
        TaxonBase<?> taxonBase = super.find(lsid);
1260
        if(taxonBase != null) {
1261
            List<String> propertyPaths = new ArrayList<>();
1262
            propertyPaths.add("createdBy");
1263
            propertyPaths.add("updatedBy");
1264
            propertyPaths.add("name");
1265
            propertyPaths.add("sec");
1266
            propertyPaths.add("relationsToThisTaxon");
1267
            propertyPaths.add("relationsToThisTaxon.fromTaxon");
1268
            propertyPaths.add("relationsToThisTaxon.toTaxon");
1269
            propertyPaths.add("relationsFromThisTaxon");
1270
            propertyPaths.add("relationsFromThisTaxon.toTaxon");
1271
            propertyPaths.add("relationsToThisTaxon.type");
1272
            propertyPaths.add("synonyms");
1273
            propertyPaths.add("synonyms.type");
1274
            propertyPaths.add("descriptions");
1275

    
1276
            defaultBeanInitializer.initialize(taxonBase, propertyPaths);
1277
        }
1278
        return taxonBase;
1279
    }
1280

    
1281
    @Override
1282
    public List<String> taxaByNameNotInDB(List<String> taxonNames){
1283
        //get all taxa, already in db
1284
        Query query = getSession().createQuery(
1285
                 " FROM TaxonName t "
1286
                +" WHERE t.nameCache IN (:taxonList)");
1287
        query.setParameterList("taxonList", taxonNames);
1288
        @SuppressWarnings("unchecked")
1289
        List<TaxonName> taxaInDB = query.list();
1290
        //compare the original list with the result of the query
1291
        for (TaxonName taxonName: taxaInDB){
1292
            String nameCache = taxonName.getNameCache();
1293
            if (taxonNames.contains(nameCache)){
1294
                taxonNames.remove(nameCache);
1295
            }
1296
        }
1297

    
1298
        return taxonNames;
1299
    }
1300

    
1301
    //TODO: mal nur mit UUID probieren (ohne fetch all properties), vielleicht geht das schneller?
1302
    @Override
1303
    public List<UUID> findIdenticalTaxonNameIds(List<String> propertyPaths){
1304
        Query query=getSession().createQuery(
1305
                   "SELECT tmb2 "
1306
                + " FROM ZoologicalName tmb, ZoologicalName tmb2 FETCH ALL properties "
1307
                + " WHERE tmb.id != tmb2.id AND tmb.nameCache = tmb2.nameCache");
1308
        @SuppressWarnings("unchecked")
1309
        List<UUID> zooNames = query.list();
1310

    
1311
        return zooNames;
1312

    
1313
    }
1314

    
1315
    @Override
1316
    public List<TaxonName> findIdenticalTaxonNames(List<String> propertyPaths) {
1317

    
1318
        Query query=getSession().createQuery(
1319
                  " SELECT tmb2 "
1320
                + " FROM ZoologicalName tmb, ZoologicalName tmb2 FETCH ALL properties "
1321
                + " WHERE tmb.id != tmb2.id AND tmb.nameCache = tmb2.nameCache");
1322

    
1323
        @SuppressWarnings("unchecked")
1324
        List<TaxonName> zooNames = query.list();
1325

    
1326
        TaxonNameComparator taxComp = new TaxonNameComparator();
1327
        Collections.sort(zooNames, taxComp);
1328

    
1329
        for (TaxonName taxonName: zooNames){
1330
            defaultBeanInitializer.initialize(taxonName, propertyPaths);
1331
        }
1332

    
1333
        return zooNames;
1334
    }
1335

    
1336
    @Override
1337
    public List<TaxonName> findIdenticalNamesNew(List<String> propertyPaths){
1338

    
1339
        //Hole die beiden Source_ids von "Fauna Europaea" und "Erms" und in sources der names darf jeweils nur das entgegengesetzte auftreten (i member of tmb.taxonBases)
1340
        Query query = getSession().createQuery("SELECT id "
1341
                + "FROM Reference "
1342
                + " WHERE titleCache LIKE 'Fauna Europaea database'");
1343
        @SuppressWarnings("unchecked")
1344
        List<String> secRefFauna = query.list();
1345
        query = getSession().createQuery("Select id from Reference where titleCache like 'ERMS'");
1346
        @SuppressWarnings("unchecked")
1347
        List<String> secRefErms = query.list();
1348
        //Query query = getSession().createQuery("select tmb2.nameCache from ZoologicalName tmb, TaxonBase tb1, ZoologicalName tmb2, TaxonBase tb2 where tmb.id != tmb2.id and tb1.name = tmb and tb2.name = tmb2 and tmb.nameCache = tmb2.nameCache and tb1.sec != tb2.sec");
1349
        //Get all names of fauna europaea
1350
        query = getSession().createQuery("select zn.nameCache from ZoologicalName zn, TaxonBase tb where tb.name = zn and tb.sec.id = :secRefFauna");
1351
        query.setParameter("secRefFauna", secRefFauna.get(0));
1352
        @SuppressWarnings("unchecked")
1353
        List<String> namesFauna= query.list();
1354

    
1355
        //Get all names of erms
1356

    
1357
        query = getSession().createQuery("select zn.nameCache from ZoologicalName zn, TaxonBase tb where tb.name = zn and tb.sec.id = :secRefErms");
1358
        query.setParameter("secRefErms", secRefErms.get(0));
1359

    
1360
        @SuppressWarnings("unchecked")
1361
        List<String> namesErms = query.list();
1362
        /*TaxonNameComparator comp = new TaxonNameComparator();
1363
        Collections.sort(namesFauna);
1364
        Collections.sort(namesErms);
1365
        */
1366
        List <String> identicalNames = new ArrayList<>();
1367

    
1368
        for (String nameFauna: namesFauna){
1369
            if (namesErms.contains(nameFauna)){
1370
                identicalNames.add(nameFauna);
1371
            }
1372
        }
1373

    
1374
        query = getSession().createQuery("from ZoologicalName zn where zn.nameCache IN (:identicalNames)");
1375
        query.setParameterList("identicalNames", identicalNames);
1376
        List<TaxonName> result = query.list();
1377
        TaxonName tempName = result.get(0);
1378

    
1379
        Iterator<IdentifiableSource> sources = tempName.getSources().iterator();
1380

    
1381
        TaxonNameComparator taxComp = new TaxonNameComparator();
1382
        Collections.sort(result, taxComp);
1383
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1384
        return result;
1385

    
1386
    }
1387

    
1388
//
1389
//
1390
//    @Override
1391
//    public String getPhylumName(TaxonName name){
1392
//        List results = new ArrayList();
1393
//        try{
1394
//        Query query = getSession().createSQLQuery("select getPhylum("+ name.getId()+");");
1395
//        results = query.list();
1396
//        }catch(Exception e){
1397
//            System.err.println(name.getUuid());
1398
//            return null;
1399
//        }
1400
//        System.err.println("phylum of "+ name.getTitleCache() );
1401
//        return (String)results.get(0);
1402
//    }
1403

    
1404

    
1405
    @Override
1406
    public long countTaxaByCommonName(String searchString,
1407
            Classification classification, MatchMode matchMode,
1408
            Set<NamedArea> namedAreas) {
1409
        boolean doCount = true;
1410
        Query query = prepareTaxaByCommonName(searchString, classification, matchMode, namedAreas, null, null, doCount, false);
1411
        if (query != null && !query.list().isEmpty()) {
1412
            Object o = query.uniqueResult();
1413
            if(o != null) {
1414
                return (Long)o;
1415
            }
1416
        }
1417
        return 0;
1418
    }
1419

    
1420
    private Subselects createByNameHQLString(boolean doConceptRelations,
1421
                boolean includeUnpublished, Classification classification,  Set<NamedArea> areasExpanded,
1422
                MatchMode matchMode, String searchField){
1423

    
1424
        boolean doAreaRestriction = areasExpanded.size() > 0;
1425
        String doAreaRestrictionSubSelect =
1426
                     " SELECT %s.id "
1427
                   + " FROM Distribution e "
1428
                   + "    JOIN e.inDescription d "
1429
                   + "    JOIN d.taxon t " +
1430
                (classification != null ? " JOIN t.taxonNodes AS tn " : " ");
1431

    
1432
        String doAreaRestrictionConceptRelationSubSelect =
1433
                   "SELECT %s.id "
1434
                   + " FROM Distribution e "
1435
                   + "   JOIN e.inDescription d"
1436
                   + "   JOIN d.taxon t";
1437

    
1438
        String doTaxonSubSelect =
1439
                     " SELECT %s.id "
1440
                   + " FROM Taxon t " + (classification != null ? " "
1441
                           + " JOIN t.taxonNodes AS tn " : " ");
1442

    
1443
        String doTaxonMisappliedNameSubSelect =
1444
                     " SELECT %s.id "
1445
                   + " FROM Taxon t ";
1446

    
1447
        String doTaxonNameJoin = " JOIN t.name n ";
1448

    
1449
        String doSynonymNameJoin =
1450
                   " JOIN t.synonyms s "
1451
                 + " JOIN s.name sn";
1452

    
1453
        String doConceptRelationJoin =
1454
                   " LEFT JOIN t.relationsFromThisTaxon AS rft " +
1455
                   " LEFT JOIN rft.relatedTo AS rt " +
1456
                      (classification != null ? " LEFT JOIN rt.taxonNodes AS tn2 " : " ") +
1457
                   " LEFT JOIN rt.name AS n2" +
1458
                   " LEFT JOIN rft.type as rtype";
1459

    
1460
        String doCommonNamesJoin =
1461
                   " JOIN t.descriptions AS description "+
1462
                   " LEFT JOIN description.descriptionElements AS com " +
1463
                   " LEFT JOIN com.feature f ";
1464

    
1465

    
1466
        String doClassificationWhere = " tn.classification = :classification";
1467
        String doClassificationForConceptRelationsWhere = " tn2.classification = :classification";
1468

    
1469
        String doAreaRestrictionWhere =  " e.area.uuid in (:namedAreasUuids)";
1470
        String doCommonNamesRestrictionWhere = " (f.supportsCommonTaxonName = true and com.name "+matchMode.getMatchOperator()+" :queryString )";
1471

    
1472
        String doSearchFieldWhere = "%s." + searchField + " " + matchMode.getMatchOperator() + " :queryString";
1473

    
1474
        String doRelationshipTypeComparison = " rtype in (:rTypeSet) ";
1475

    
1476
        String taxonSubselect = null;
1477
        String synonymSubselect = null;
1478
        String conceptSelect = null;
1479
        String commonNameSubselect = null;
1480

    
1481
        if(classification != null ){
1482
            if (!doConceptRelations){
1483
                if(doAreaRestriction){
1484
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
1485
                            " WHERE " + doAreaRestrictionWhere +
1486
                            "  AND " + doClassificationWhere +
1487
                            "  AND " + String.format(doSearchFieldWhere, "n");
1488
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
1489
                            " WHERE " + doAreaRestrictionWhere +
1490
                            "  AND " + doClassificationWhere +
1491
                            "  AND " + String.format(doSearchFieldWhere, "sn");
1492
                    commonNameSubselect =  String.format(doAreaRestrictionSubSelect, "t") + doCommonNamesJoin +
1493
                            " WHERE " +  doAreaRestrictionWhere +
1494
                            "  AND " + doClassificationWhere +
1495
                            "  AND " + String.format(doSearchFieldWhere, "n") +
1496
                            "  AND " + doCommonNamesRestrictionWhere;
1497
                } else {//no area restriction
1498
                    taxonSubselect = String.format(doTaxonSubSelect, "t" )+ doTaxonNameJoin +
1499
                            " WHERE " + doClassificationWhere +
1500
                            "  AND " + String.format(doSearchFieldWhere, "n");
1501
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
1502
                            " WHERE " + doClassificationWhere +
1503
                            "  AND " + String.format(doSearchFieldWhere, "sn");
1504
                    commonNameSubselect =String.format(doTaxonSubSelect, "t" )+ doCommonNamesJoin +
1505
                            " WHERE " + doClassificationWhere +
1506
                            "  AND " + doCommonNamesRestrictionWhere;
1507
                }
1508
            }else{ //concept relations included
1509
                if(doAreaRestriction){
1510
                    conceptSelect = String.format(doAreaRestrictionConceptRelationSubSelect, "t") + doTaxonNameJoin + doConceptRelationJoin  +
1511
                            " WHERE " + doAreaRestrictionWhere +
1512
                            "  AND " + String.format(doSearchFieldWhere, "n") +
1513
                            "  AND " + doClassificationForConceptRelationsWhere +
1514
                            "  AND " + doRelationshipTypeComparison;
1515
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
1516
                            " WHERE " + doAreaRestrictionWhere +
1517
                            "  AND " + String.format(doSearchFieldWhere, "n") +
1518
                            "  AND " + doClassificationWhere;
1519
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
1520
                            " WHERE " + doAreaRestrictionWhere +
1521
                            "  AND " + doClassificationWhere +
1522
                            "  AND " + String.format(doSearchFieldWhere, "sn");
1523
                    commonNameSubselect= String.format(doAreaRestrictionSubSelect, "t")+ doCommonNamesJoin +
1524
                            " WHERE " + doAreaRestrictionWhere +
1525
                            "  AND " + doClassificationWhere +
1526
                            "  AND " + doCommonNamesRestrictionWhere;
1527
                } else {//no area restriction
1528
                    conceptSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doConceptRelationJoin +
1529
                            " WHERE " + String.format(doSearchFieldWhere, "n") +
1530
                            "  AND " + doClassificationForConceptRelationsWhere +
1531
                            "  AND " + doRelationshipTypeComparison;
1532
                    taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin +
1533
                            " WHERE " +  String.format(doSearchFieldWhere, "n") +
1534
                            " AND "+ doClassificationWhere;
1535
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
1536
                            " WHERE " + doClassificationWhere +
1537
                            "  AND " +  String.format(doSearchFieldWhere, "sn");
1538
                    commonNameSubselect= String.format(doTaxonSubSelect, "t")+ doCommonNamesJoin +
1539
                            " WHERE " + doClassificationWhere +
1540
                            "  AND " + doCommonNamesRestrictionWhere;
1541
                }
1542
            }
1543
        } else { //classification = null
1544
            if(doAreaRestriction){
1545
                conceptSelect = String.format(doAreaRestrictionConceptRelationSubSelect, "t") + doTaxonNameJoin + doConceptRelationJoin +
1546
                        " WHERE " + doAreaRestrictionWhere +
1547
                        "  AND " + String.format(doSearchFieldWhere, "n")+
1548
                        "  AND " + doRelationshipTypeComparison;
1549
                taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
1550
                        " WHERE " + doAreaRestrictionWhere +
1551
                        "  AND " + String.format(doSearchFieldWhere, "n");
1552
                synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
1553
                        " WHERE " + doAreaRestrictionWhere +
1554
                        "  AND " + String.format(doSearchFieldWhere, "sn");
1555
                commonNameSubselect = String.format(doAreaRestrictionSubSelect, "t")+ doCommonNamesJoin +
1556
                        " WHERE " + doAreaRestrictionWhere +
1557
                        "  AND " + doCommonNamesRestrictionWhere;
1558
            } else { //no area restriction
1559
                conceptSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doConceptRelationJoin +
1560
                        " WHERE " +  String.format(doSearchFieldWhere, "n") +
1561
                        " AND " + doRelationshipTypeComparison;
1562
                taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin +
1563
                        " WHERE " +  String.format(doSearchFieldWhere, "n");
1564
                synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
1565
                        " WHERE " +  String.format(doSearchFieldWhere, "sn");
1566
                commonNameSubselect = String.format(doTaxonSubSelect, "t" ) +doCommonNamesJoin +
1567
                        " WHERE "+  doCommonNamesRestrictionWhere;
1568
            }
1569
        }
1570

    
1571
        if (!includeUnpublished){
1572
            taxonSubselect   += " AND t.publish = :publish ";
1573
            synonymSubselect += " AND s.publish = :publish AND t.publish = :publish ";
1574
            commonNameSubselect += " AND t.publish = :publish ";
1575
            conceptSelect += " AND t.publish = :publish AND rt.publish = :publish ";
1576
        }
1577

    
1578
        Subselects result = new Subselects(taxonSubselect, synonymSubselect, conceptSelect, commonNameSubselect);
1579
        return result;
1580
    }
1581

    
1582
    private class Subselects{
1583
        String taxonSubselect;
1584
        String synonymSubselect;
1585
        String conceptSelect;
1586
        String commonNameSubselect;
1587
        private Subselects(String taxonSubselect, String synonymSubselect, String conceptSelect,
1588
                String commonNameSubselect) {
1589
            this.taxonSubselect = taxonSubselect;
1590
            this.synonymSubselect = synonymSubselect;
1591
            this.conceptSelect = conceptSelect;
1592
            this.commonNameSubselect = commonNameSubselect;
1593
        }
1594
    }
1595

    
1596
	@Override
1597
	public List<UuidAndTitleCache<Taxon>> getTaxaByCommonNameForEditor(
1598
			String titleSearchStringSqlized, Classification classification,
1599
			MatchMode matchMode, Set<NamedArea> namedAreas) {
1600
		Query query = prepareTaxaByCommonName(titleSearchStringSqlized, classification, matchMode, namedAreas, null, null, false, true);
1601
        if (query != null){
1602
            @SuppressWarnings("unchecked")
1603
            List<Object> resultArray = query.list();
1604
            List<UuidAndTitleCache<Taxon>> returnResult = new ArrayList<>() ;
1605
            Object[] result;
1606
            for(int i = 0; i<resultArray.size();i++){
1607
            	result = (Object[]) resultArray.get(i);
1608
            	returnResult.add(new UuidAndTitleCache<Taxon>(Taxon.class, (UUID) result[0],(Integer)result[1], (String)result[2], new Boolean(result[4].toString()), null));
1609
            }
1610
            return returnResult;
1611
        }else{
1612
            return new ArrayList<>();
1613
        }
1614
	}
1615

    
1616

    
1617
	/**
1618
	 * @param
1619
	 * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#countByIdentifier(java.lang.Class, java.lang.String, eu.etaxonomy.cdm.model.common.DefinedTerm, eu.etaxonomy.cdm.model.taxon.TaxonNode, eu.etaxonomy.cdm.persistence.query.MatchMode)
1620
	 */
1621
	@Override
1622
	public <S extends TaxonBase> long countByIdentifier(Class<S> clazz,
1623
			String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter, MatchMode matchmode) {
1624
		if (subtreeFilter == null){
1625
			return countByIdentifier(clazz, identifier, identifierType, matchmode);
1626
		}
1627

    
1628
		Class<?> clazzParam = (clazz == null) ? type : clazz;
1629
		checkNotInPriorView("TaxonDaoHibernateImpl.countByIdentifier(T clazz, String identifier, DefinedTerm identifierType, TaxonNode subMatchMode matchmode)");
1630

    
1631
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
1632
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
1633

    
1634
		getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
1635
		String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
1636
		String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
1637
		String synTreeJoin = isSynonym ? " LEFT JOIN c.acceptedTaxon as acc LEFT JOIN acc.taxonNodes synTn  " : "";
1638
		String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
1639
		String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
1640

    
1641
		String queryString = "SELECT count(*)  FROM %s as c " +
1642
                " INNER JOIN c.identifiers as ids " +
1643
                accTreeJoin +
1644
                synTreeJoin +
1645
                " WHERE (1=1) " +
1646
                	"  AND ( " + accWhere + " OR " + synWhere + ")";
1647
		queryString = String.format(queryString, clazzParam.getSimpleName());
1648

    
1649
		if (identifier != null){
1650
			if (matchmode == null || matchmode == MatchMode.EXACT){
1651
				queryString += " AND ids.identifier = '"  + identifier + "'";
1652
			}else {
1653
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
1654
			}
1655
		}
1656
		if (identifierType != null){
1657
        	queryString += " AND ids.type = :type";
1658
        }
1659

    
1660
		Query query = getSession().createQuery(queryString);
1661
        if (identifierType != null){
1662
        	query.setEntity("type", identifierType);
1663
        }
1664

    
1665
		return (Long)query.uniqueResult();
1666
	}
1667

    
1668
	@Override
1669
	public <S extends TaxonBase> List<Object[]> findByIdentifier(
1670
			Class<S> clazz, String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter,
1671
			MatchMode matchmode, boolean includeEntity,
1672
			Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
1673

    
1674
		checkNotInPriorView("TaxonDaoHibernateImpl.findByIdentifier(T clazz, String identifier, DefinedTerm identifierType, MatchMode matchmode, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)");
1675
		Class<?> clazzParam = clazz == null ? type : clazz;
1676

    
1677
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
1678
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
1679
		getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
1680
		String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
1681
		String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
1682
		String synTreeJoin = isSynonym ? " LEFT JOIN c.acceptedTaxon as acc LEFT JOIN acc.taxonNodes synTn  " : "";
1683
		String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
1684
		String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
1685

    
1686
		String queryString = " SELECT ids.type, ids.identifier, %s " +
1687
				" FROM %s as c " +
1688
                " INNER JOIN c.identifiers as ids " +
1689
                accTreeJoin +
1690
				synTreeJoin +
1691
                " WHERE (1=1) " +
1692
                	" AND ( " + accWhere + " OR " + synWhere + ")";
1693
		queryString = String.format(queryString, (includeEntity ? "c":"c.uuid, c.titleCache") , clazzParam.getSimpleName());
1694

    
1695
		//Matchmode and identifier
1696
		if (identifier != null){
1697
			if (matchmode == null || matchmode == MatchMode.EXACT){
1698
				queryString += " AND ids.identifier = '"  + identifier + "'";
1699
			}else {
1700
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
1701
			}
1702
		}
1703
        if (identifierType != null){
1704
        	queryString += " AND ids.type = :type";
1705
        }
1706
        //order
1707
        queryString +=" ORDER BY ids.type.uuid, ids.identifier, c.uuid ";
1708

    
1709
		Query query = getSession().createQuery(queryString);
1710

    
1711
		//parameters
1712
		if (identifierType != null){
1713
        	query.setEntity("type", identifierType);
1714
        }
1715

    
1716
        //paging
1717
        setPagingParameter(query, pageSize, pageNumber);
1718

    
1719
        List<Object[]> results = query.list();
1720
        //initialize
1721
        if (includeEntity){
1722
        	List<S> entities = new ArrayList<S>();
1723
        	for (Object[] result : results){
1724
        		entities.add((S)result[2]);
1725
        	}
1726
        	defaultBeanInitializer.initializeAll(entities, propertyPaths);
1727
        }
1728
        return results;
1729
	}
1730

    
1731
    /**
1732
     * {@inheritDoc}
1733
     * @see #countByIdentifier(Class, String, DefinedTerm, TaxonNode, MatchMode)
1734
     */
1735
    @Override
1736
    public <S extends TaxonBase> long countByMarker(Class<S> clazz, MarkerType markerType,
1737
            Boolean markerValue, TaxonNode subtreeFilter) {
1738
        if (markerType == null){
1739
            return 0;
1740
        }
1741

    
1742
        if (subtreeFilter == null){
1743
            return countByMarker(clazz, markerType, markerValue);
1744
        }
1745

    
1746
        Class<?> clazzParam = clazz == null ? type : clazz;
1747
        checkNotInPriorView("TaxonDaoHibernateImpl.countByMarker(Class<S> clazz, DefinedTerm markerType, boolean markerValue, TaxonNode subtreeFilter)");
1748

    
1749
        boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
1750
        boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
1751

    
1752
        getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
1753
        String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
1754
        String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
1755
        String synTreeJoin = isSynonym ? " LEFT JOIN c.acceptedTaxon acc LEFT JOIN acc.taxonNodes synTn  " : "";
1756
        String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
1757
        String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
1758

    
1759
        String queryString = "SELECT count(*)  FROM %s as c " +
1760
                " INNER JOIN c.markers as mks " +
1761
                accTreeJoin +
1762
                synTreeJoin +
1763
                " WHERE (1=1) " +
1764
                    "  AND ( " + accWhere + " OR " + synWhere + ")";
1765
        queryString = String.format(queryString, clazzParam.getSimpleName());
1766

    
1767
        if (markerValue != null){
1768
            queryString += " AND mks.flag = :flag";
1769
        }
1770
        if (markerType != null){
1771
            queryString += " AND mks.markerType = :type";
1772
        }
1773

    
1774
        Query query = getSession().createQuery(queryString);
1775
        if (markerType != null){
1776
            query.setEntity("type", markerType);
1777
        }
1778
        if (markerValue != null){
1779
            query.setBoolean("flag", markerValue);
1780
        }
1781

    
1782
        Long c = (Long)query.uniqueResult();
1783
        return c;
1784
    }
1785

    
1786
    /**
1787
     * {@inheritDoc}
1788
     */
1789
    @Override
1790
    public <S extends TaxonBase> List<Object[]> findByMarker(Class<S> clazz, MarkerType markerType,
1791
            Boolean markerValue, TaxonNode subtreeFilter, boolean includeEntity,
1792
            TaxonTitleType titleType, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
1793
        checkNotInPriorView("TaxonDaoHibernateImpl.findByMarker(T clazz, String identifier, DefinedTerm identifierType, MatchMode matchmode, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)");
1794
        if (markerType == null){
1795
            return new ArrayList<Object[]>();
1796
        }
1797
        if (titleType == null){
1798
            titleType = TaxonTitleType.DEFAULT();
1799
        }
1800

    
1801
        Class<?> clazzParam = clazz == null ? type : clazz;
1802

    
1803
        boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
1804
        boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
1805
        getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
1806
        String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
1807
        String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
1808
        String synTreeJoin = isSynonym ? " LEFT JOIN c.acceptedTaxon as acc LEFT JOIN acc.taxonNodes synTn  " : "";
1809
        String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
1810
        String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
1811
        String selectParams = includeEntity ? "c" : titleType.hqlReplaceSelect("c.uuid, c.titleCache", "c.titleCache");
1812
        String titleTypeJoin = includeEntity ? "" : titleType.hqlJoin();
1813

    
1814
        String queryString = "SELECT mks.markerType, mks.flag, %s " +
1815
                " FROM %s as c " +
1816
                " INNER JOIN c.markers as mks " +
1817
                titleTypeJoin +
1818
                accTreeJoin +
1819
                synTreeJoin +
1820
                " WHERE (1=1) " +
1821
                    " AND ( " + accWhere + " OR " + synWhere + ")";
1822
        queryString = String.format(queryString, selectParams, clazzParam.getSimpleName());
1823

    
1824
        //type and value
1825
        if (markerValue != null){
1826
            queryString += " AND mks.flag = :flag";
1827
        }
1828
        queryString += " AND mks.markerType = :type";
1829
        //order
1830
        queryString +=" ORDER BY mks.markerType.uuid, mks.flag, c.uuid ";
1831

    
1832
        Query query = getSession().createQuery(queryString);
1833

    
1834
        //parameters
1835
        query.setEntity("type", markerType);
1836
        if (markerValue != null){
1837
            query.setBoolean("flag", markerValue);
1838
        }
1839

    
1840
        //paging
1841
        setPagingParameter(query, pageSize, pageNumber);
1842

    
1843
        List<Object[]> results = query.list();
1844
        //initialize
1845
        if (includeEntity){
1846
            List<S> entities = new ArrayList<S>();
1847
            for (Object[] result : results){
1848
                entities.add((S)result[2]);
1849
            }
1850
            defaultBeanInitializer.initializeAll(entities, propertyPaths);
1851
        }
1852
        return results;
1853
    }
1854

    
1855
    @Override
1856
    public long countTaxonRelationships(Set<TaxonRelationshipType> types) {
1857
        Criteria criteria = getSession().createCriteria(TaxonRelationship.class);
1858

    
1859
        if (types != null) {
1860
            if (types.isEmpty()){
1861
                return 0l;
1862
            }else{
1863
                criteria.add(Restrictions.in("type", types) );
1864
            }
1865
        }
1866
        //count
1867
        criteria.setProjection(Projections.rowCount());
1868
        long result = (Long)criteria.uniqueResult();
1869

    
1870
        return result;
1871
    }
1872

    
1873
    @Override
1874
    public List<TaxonRelationship> getTaxonRelationships(Set<TaxonRelationshipType> types,
1875
            Integer pageSize, Integer pageNumber,
1876
            List<OrderHint> orderHints, List<String> propertyPaths) {
1877
        Criteria criteria = getCriteria(TaxonRelationship.class);
1878

    
1879
        if (types != null) {
1880
            if (types.isEmpty()){
1881
                return new ArrayList<>();
1882
            }else{
1883
                criteria.add(Restrictions.in("type", types) );
1884
            }
1885
        }
1886
        addOrder(criteria,orderHints);
1887
        addPageSizeAndNumber(criteria, pageSize, pageNumber);
1888

    
1889
        @SuppressWarnings("unchecked")
1890
        List<TaxonRelationship> results = criteria.list();
1891
        defaultBeanInitializer.initializeAll(results, propertyPaths);
1892

    
1893
        return results;
1894
    }
1895

    
1896
    @Override
1897
    public  List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCache(Integer limit, String pattern){
1898
        Session session = getSession();
1899
        Query query = null;
1900
        if (pattern != null){
1901
            query = session.createQuery(
1902
                  " SELECT tb.uuid, tb.id, tb.titleCache, tb.name.rank "
1903
                  + " FROM TaxonBase as tb "
1904
                  + " WHERE tb.titleCache LIKE :pattern");
1905
            pattern = pattern.replace("*", "%");
1906
            pattern = pattern.replace("?", "_");
1907
            pattern = pattern + "%";
1908
            query.setParameter("pattern", pattern);
1909
        } else {
1910
            query = session.createQuery(
1911
                    " SELECT tb.uuid, taxonBase.id, tb.titleCache, tb.name.rank "
1912
                  + " FROM TaxonBase AS tb");
1913
        }
1914
        if (limit != null){
1915
           query.setMaxResults(limit);
1916
        }
1917

    
1918
        return getUuidAndTitleCache(query);
1919
    }
1920

    
1921
    @Override
1922
    protected List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCache(Query query){
1923
        List<UuidAndTitleCache<TaxonBase>> list = new ArrayList<>();
1924

    
1925
        @SuppressWarnings("unchecked")
1926
        List<Object[]> result = query.list();
1927
        if (!result.isEmpty()){
1928
            if (result.iterator().next().length == 4){
1929
                Collections.sort(result, new UuidAndTitleCacheTaxonComparator());
1930
            }
1931
        }
1932

    
1933
        for(Object[] object : result){
1934
            list.add(new UuidAndTitleCache<TaxonBase>((UUID) object[0],(Integer) object[1], (String) object[2]));
1935
        }
1936
        return list;
1937
    }
1938

    
1939

    
1940
}
(3-3/5)