Project

General

Profile

Download (77.7 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 extends IdentifiableDaoBase<TaxonBase> implements ITaxonDao {
83
//    private AlternativeSpellingSuggestionParser<TaxonBase> alternativeSpellingSuggestionParser;
84
    private static final Logger logger = Logger.getLogger(TaxonDaoHibernateImpl.class);
85

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

    
94
    @Autowired
95
    private ITaxonNameDao taxonNameDao;
96

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

    
103

    
104
    @Override
105
    public List<TaxonBase> getTaxaByName(String queryString, Reference sec) {
106

    
107
        return getTaxaByName(queryString, true, sec);
108
    }
109

    
110
    @Override
111
    public List<TaxonBase> getTaxaByName(String queryString, Boolean accepted, Reference sec) {
112
        checkNotInPriorView("TaxonDaoHibernateImpl.getTaxaByName(String name, Reference sec)");
113

    
114
        Criteria criteria = null;
115
        if (accepted == true) {
116
            criteria = getSession().createCriteria(Taxon.class);
117
        } else {
118
            criteria = getSession().createCriteria(Synonym.class);
119
        }
120

    
121
        criteria.setFetchMode( "name", FetchMode.JOIN );
122
        criteria.createAlias("name", "name");
123

    
124
        if (sec != null && sec.getId() != 0) {
125
            criteria.add(Restrictions.eq("sec", sec ) );
126
        }
127

    
128
        if (queryString != null) {
129
            criteria.add(Restrictions.ilike("name.nameCache", queryString));
130
        }
131

    
132
        return criteria.list();
133
    }
134

    
135
    public List<TaxonBase> getTaxaByName(boolean doTaxa, boolean doSynonyms, String queryString, MatchMode matchMode,
136
            Integer pageSize, Integer pageNumber) {
137

    
138
        return getTaxaByName(doTaxa, doSynonyms, false, false, false, queryString, null, matchMode, null, null, pageSize, pageNumber, null);
139
    }
140

    
141
    @Override
142
    public List<TaxonBase> getTaxaByName(String queryString, MatchMode matchMode,
143
            Boolean accepted, Integer pageSize, Integer pageNumber) {
144

    
145
        boolean doTaxa = true;
146
        boolean doSynonyms = true;
147

    
148
        if (accepted == true) {
149
            doSynonyms = false;
150
        } else {
151
           doTaxa = false;
152
        }
153
        return getTaxaByName(doTaxa, doSynonyms, queryString, matchMode, pageSize, pageNumber);
154
    }
155

    
156
    @Override
157
    public List<TaxonBase> getTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, boolean doCommonNames,
158
            boolean includeAuthors,
159
            String queryString, Classification classification,
160
            MatchMode matchMode, Set<NamedArea> namedAreas, NameSearchOrder order,
161
            Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
162

    
163
        boolean doCount = false;
164

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

    
168
        if (query != null){
169
            @SuppressWarnings("unchecked")
170
            List<TaxonBase> results = query.list();
171

    
172
            defaultBeanInitializer.initializeAll(results, propertyPaths);
173

    
174
            //Collections.sort(results, comp);
175
            return results;
176
        }
177

    
178
        return new ArrayList<>();
179

    
180
    }
181

    
182

    
183
    //new search for the editor, for performance issues the return values are only uuid and titleCache, to avoid the initialisation of all objects
184
    @Override
185
    @SuppressWarnings("unchecked")
186
    public List<UuidAndTitleCache<IdentifiableEntity>> getTaxaByNameForEditor(boolean doTaxa, boolean doSynonyms, boolean doNamesWithoutTaxa, boolean doMisappliedNames, boolean doCommonNames, String queryString, Classification classification,
187
            MatchMode matchMode, Set<NamedArea> namedAreas, NameSearchOrder order) {
188
//        long zstVorher;
189
//        long zstNachher;
190
        if (order == null){
191
            order = NameSearchOrder.ALPHA;  //TODO add to signature
192
        }
193

    
194
        boolean doCount = false;
195
        boolean includeAuthors = false;
196
        List<UuidAndTitleCache<IdentifiableEntity>> resultObjects = new ArrayList<UuidAndTitleCache<IdentifiableEntity>>();
197
        if (doNamesWithoutTaxa){
198
        	List<? extends TaxonName> nameResult = taxonNameDao.findByName(
199
        	        includeAuthors, queryString, matchMode, null, null, null, null);
200

    
201
        	for (TaxonName name: nameResult){
202
        		if (name.getTaxonBases().size() == 0){
203
        			resultObjects.add(new UuidAndTitleCache(TaxonName.class, name.getUuid(), name.getId(), name.getTitleCache()));
204
        		}
205
        	}
206
        	if (!doSynonyms && !doTaxa && !doCommonNames){
207
        		return resultObjects;
208
        	}
209
        }
210
        Query query = prepareTaxaByNameForEditor(doTaxa, doSynonyms, doMisappliedNames, doCommonNames, "nameCache", queryString, classification, matchMode, namedAreas, doCount, order);
211

    
212

    
213
        if (query != null){
214
            List<Object[]> results = query.list();
215

    
216
            Object[] result;
217
            for(int i = 0; i<results.size();i++){
218
                result = results.get(i);
219

    
220
                //differentiate taxa and synonyms
221
                // new Boolean(result[3].toString()) is due to the fact that result[3] could be a Boolean ora String
222
                // see FIXME in 'prepareQuery' for more details
223
                if (doTaxa || doSynonyms || doCommonNames){
224
                    if (result[3].equals("synonym")) {
225
                        resultObjects.add( new UuidAndTitleCache(Synonym.class, (UUID) result[0], (Integer) result[1], (String)result[2], new Boolean(result[4].toString()), null));
226
                    }
227
                    else {
228
                        resultObjects.add( new UuidAndTitleCache(Taxon.class, (UUID) result[0], (Integer) result[1], (String)result[2], new Boolean(result[4].toString()), null));
229
                    }
230

    
231
                }else if (doSynonyms){
232
                    resultObjects.add( new UuidAndTitleCache(Synonym.class, (UUID) result[0], (Integer) result[1], (String)result[2], new Boolean(result[4].toString()), null));
233
                }
234
            }
235

    
236

    
237

    
238
        }
239

    
240
        return resultObjects;
241

    
242
    }
243

    
244
    @Override
245
    public List<Taxon> getTaxaByCommonName(String queryString, Classification classification,
246
               MatchMode matchMode, Set<NamedArea> namedAreas, Integer pageSize,
247
               Integer pageNumber, List<String> propertyPaths) {
248
        boolean doCount = false;
249
        Query query = prepareTaxaByCommonName(queryString, classification, matchMode, namedAreas, pageSize, pageNumber, doCount, false);
250
        if (query != null){
251
            List<Taxon> results = query.list();
252
            defaultBeanInitializer.initializeAll(results, propertyPaths);
253
            return results;
254
        }
255
        return new ArrayList<Taxon>();
256

    
257
    }
258

    
259
    /**
260
     * @param clazz
261
     * @param searchField the field in TaxonName to be searched through usually either <code>nameCache</code> or <code>titleCache</code>
262
     * @param queryString
263
     * @param classification TODO
264
     * @param matchMode
265
     * @param namedAreas
266
     * @param pageSize
267
     * @param pageNumber
268
     * @param doCount
269
     * @return
270
     *
271
     *
272
     */
273
    private Query prepareTaxaByNameForEditor(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, boolean doCommonNames,
274
            String searchField, String queryString, Classification classification,
275
            MatchMode matchMode, Set<NamedArea> namedAreas, boolean doCount, NameSearchOrder order) {
276
        return prepareQuery(doTaxa, doSynonyms, doMisappliedNames, doCommonNames, searchField, queryString,
277
                classification, matchMode, namedAreas, order, doCount, true);
278
    }
279

    
280
    /**
281
     * @param searchField
282
     * @param queryString
283
     * @param classification
284
     * @param matchMode
285
     * @param namedAreas
286
     * @param doCount
287
     * @param doNotReturnFullEntities
288
     *            if set true the seach method will not return synonym and taxon
289
     *            entities but an array containing the uuid, titleCache, and the
290
     *            DTYPE in lowercase letters.
291
     * @param order
292
     * @param clazz
293
     * @return
294
     */
295
    private Query prepareQuery(boolean doTaxa, boolean doSynonyms, boolean doIncludeMisappliedNames, boolean doCommonNames, String searchField, String queryString,
296
                Classification classification, MatchMode matchMode, Set<NamedArea> namedAreas, NameSearchOrder order, boolean doCount, boolean doNotReturnFullEntities){
297

    
298
            if (order == null){
299
                order = NameSearchOrder.DEFAULT();
300
            }
301
            String hqlQueryString = matchMode.queryStringFrom(queryString);
302
            String selectWhat;
303
            if (doNotReturnFullEntities){
304
                selectWhat = "t.uuid, t.id, t.titleCache ";
305
            }else {
306
                selectWhat = (doCount ? "count(t)": "t");
307
            }
308

    
309

    
310

    
311
            String hql = "";
312
            Set<NamedArea> areasExpanded = new HashSet<>();
313
            if(namedAreas != null && namedAreas.size() > 0){
314
                // expand areas and restrict by distribution area
315
                Query areaQuery = getSession().createQuery("select childArea from NamedArea as childArea left join childArea.partOf as parentArea where parentArea = :area");
316
                expandNamedAreas(namedAreas, areasExpanded, areaQuery);
317
            }
318
            boolean doAreaRestriction = areasExpanded.size() > 0;
319

    
320
            Set<UUID> namedAreasUuids = new HashSet<>();
321
            for (NamedArea area:areasExpanded){
322
                namedAreasUuids.add(area.getUuid());
323
            }
324

    
325

    
326
            String [] subSelects = createHQLString(doTaxa, doSynonyms, doIncludeMisappliedNames, classification, areasExpanded, matchMode, searchField);
327
            String taxonSubselect = subSelects[1];
328
            String synonymSubselect = subSelects[2];
329
            String misappliedSelect = subSelects[0];
330
            String commonNameSubSelect = subSelects[3];
331

    
332

    
333
            if (logger.isDebugEnabled()) {
334
                logger.debug("taxonSubselect: " + (taxonSubselect != null ? taxonSubselect: "NULL"));
335
            }
336
            if (logger.isDebugEnabled()) {
337
                logger.debug("synonymSubselect: " + (synonymSubselect != null ? synonymSubselect: "NULL"));
338
            }
339

    
340
            Query subTaxon = null;
341
            Query subSynonym = null;
342
            Query subMisappliedNames = null;
343
            Query subCommonNames = null;
344

    
345
            if(doTaxa){
346
                // find Taxa
347
                subTaxon = getSession().createQuery(taxonSubselect).setParameter("queryString", hqlQueryString);
348

    
349
                if(doAreaRestriction){
350
                    subTaxon.setParameterList("namedAreasUuids", namedAreasUuids);
351
                }
352
                if(classification != null){
353
                    subTaxon.setParameter("classification", classification);
354

    
355
                }
356
            }
357

    
358
            if(doSynonyms){
359
                // find synonyms
360
                subSynonym = getSession().createQuery(synonymSubselect).setParameter("queryString", hqlQueryString);
361

    
362
                if(doAreaRestriction){
363
                    subSynonym.setParameterList("namedAreasUuids", namedAreasUuids);
364
                }
365
                if(classification != null){
366
                    subSynonym.setParameter("classification", classification);
367
                }
368
            }
369
            if (doIncludeMisappliedNames ){
370
                subMisappliedNames = getSession().createQuery(misappliedSelect).setParameter("queryString", hqlQueryString);
371
                Set<TaxonRelationshipType> relTypeSet = new HashSet<>();
372
                relTypeSet.add(TaxonRelationshipType.MISAPPLIED_NAME_FOR());
373
                relTypeSet.add(TaxonRelationshipType.PRO_PARTE_MISAPPLIED_NAME_FOR());
374
                subMisappliedNames.setParameterList("rTypeSet", relTypeSet);
375
                if(doAreaRestriction){
376
                    subMisappliedNames.setParameterList("namedAreasUuids", namedAreasUuids);
377
                }
378
                if(classification != null){
379
                    subMisappliedNames.setParameter("classification", classification);
380
                }
381
            }
382

    
383
            if(doCommonNames){
384
                // find Taxa
385
                subCommonNames = getSession().createQuery(commonNameSubSelect).setParameter("queryString", hqlQueryString);
386

    
387
                if(doAreaRestriction){
388
                    subCommonNames.setParameterList("namedAreasUuids", namedAreasUuids);
389
                }
390
                if(classification != null){
391
                    subCommonNames.setParameter("classification", classification);
392
                }
393
            }
394

    
395
            List<Integer> taxa = new ArrayList<Integer>();
396
            List<Integer> synonyms = new ArrayList<Integer>();
397
            if (doSynonyms){
398
                synonyms = subSynonym.list();
399
            }
400
            if(doTaxa){
401
                taxa = subTaxon.list();
402
            }
403
            if (doIncludeMisappliedNames){
404
                taxa.addAll(subMisappliedNames.list());
405
            }
406
            if (doCommonNames){
407
                taxa.addAll(subCommonNames.list());
408
            }
409

    
410

    
411
           // if (doTaxa && doSynonyms){
412
                if(synonyms.size()>0 && taxa.size()>0){
413
                    hql = "select " + selectWhat;
414
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
415
                    // also return the computed isOrphaned flag
416
                    if (doNotReturnFullEntities &&  !doCount ){
417
                        hql += ", case when t.id in (:taxa) then 'taxon' else 'synonym' end, " +
418
                                " case when t.id in (:taxa) and t.taxonNodes is empty and t.relationsFromThisTaxon is empty and t.relationsToThisTaxon is empty then true else false end ";
419
                    }
420
                    hql +=  " from %s t " +
421
                            " where (t.id in (:taxa) OR t.id in (:synonyms)) ";
422
                }else if (synonyms.size()>0 ){
423
                    hql = "select " + selectWhat;
424
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
425
                    // also return the computed isOrphaned flag
426
                    if (doNotReturnFullEntities &&  !doCount ){
427
                        hql += ", 'synonym', 'false' ";
428

    
429
                    }
430
                    hql +=  " from %s t " +
431
                            " where t.id in (:synonyms) ";
432

    
433
                } else if (taxa.size()>0 ){
434
                    hql = "select " + selectWhat;
435
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
436
                    // also return the computed isOrphaned flag
437
                    if (doNotReturnFullEntities &&  !doCount ){
438
                        hql += ", 'taxon', " +
439
                                " case when t.taxonNodes is empty and t.relationsFromThisTaxon is empty and t.relationsToThisTaxon is empty then true else false end ";
440
                    }
441
                    hql +=  " from %s t " +
442
                            " where t.id in (:taxa) ";
443

    
444
                } else if (StringUtils.isBlank(queryString)){
445
                    hql = "select " + selectWhat + " from %s t";
446
                } else{
447
                    return null;
448
                }
449

    
450
            String classString;
451
            if ((doTaxa || doCommonNames || doIncludeMisappliedNames) && doSynonyms){
452
                classString = "TaxonBase";
453
            } else if (doTaxa || doCommonNames){
454
                classString = "Taxon";
455
            } else if (doSynonyms && !(doCommonNames|| doTaxa || doIncludeMisappliedNames)){
456
                classString = "Synonym";
457
            } else{//only misappliedNames
458
                classString = "Taxon";
459
            }
460

    
461
            hql = String.format(hql, classString);
462

    
463

    
464
            if (hql == "") {
465
                return null;
466
            }
467
            if(!doCount){
468
                String orderBy = " ORDER BY ";
469
                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";
470

    
471
                if (order == NameSearchOrder.LENGTH_ALPHA_NAME){
472
                    orderBy += " length(t.name.nameCache), " + alphabeticBase;
473
                }else if (order == NameSearchOrder.LENGTH_ALPHA_TITLE){
474
                    orderBy += " length(t.name.titleCache), " + alphabeticBase;
475
                }else {
476
                    orderBy += alphabeticBase;
477
                }
478

    
479
                hql += orderBy;
480
            }
481

    
482
            if(logger.isDebugEnabled()){ logger.debug("hql: " + hql);}
483
            Query query = getSession().createQuery(hql);
484

    
485

    
486
            if ((doTaxa || doCommonNames || doIncludeMisappliedNames) ){
487
                // find taxa and synonyms
488
                if (taxa.size()>0){
489
                    query.setParameterList("taxa", taxa);
490
                }
491
                if (synonyms.size()>0){
492
                    query.setParameterList("synonyms",synonyms);
493
                }
494
                if (taxa.size()== 0 && synonyms.size() == 0){
495
                    return null;
496
                }
497
            }
498
            if(doSynonyms){
499
                // find synonyms
500
                if (synonyms.size()>0){
501
                    query.setParameterList("synonyms", synonyms);
502
                }else if (!doTaxa && !doCommonNames && !doIncludeMisappliedNames){
503
                    return null;
504
                }
505
            }
506

    
507
            return query;
508
    }
509

    
510

    
511
    /**
512
     * @param searchField the field in TaxonName to be searched through usually either <code>nameCache</code> or <code>titleCache</code>
513
     * @param queryString
514
     * @param classification TODO
515
     * @param matchMode
516
     * @param namedAreas
517
     * @param pageSize
518
     * @param pageNumber
519
     * @param doCount
520
     * @param clazz
521
     * @return
522
     *
523
     * FIXME implement classification restriction & implement test: see {@link TaxonDaoHibernateImplTest#testCountTaxaByName()}
524
     */
525
    private Query prepareTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, boolean doCommonNames, String searchField, String queryString,
526
            Classification classification, MatchMode matchMode, Set<NamedArea> namedAreas, NameSearchOrder order, Integer pageSize, Integer pageNumber, boolean doCount) {
527

    
528
        Query query = prepareQuery(doTaxa, doSynonyms, doMisappliedNames, doCommonNames, searchField, queryString, classification, matchMode, namedAreas, order, doCount, false);
529

    
530
        if(pageSize != null &&  !doCount && query != null) {
531
            query.setMaxResults(pageSize);
532
            if(pageNumber != null) {
533
                query.setFirstResult(pageNumber * pageSize);
534
            }
535
        }
536

    
537
        return query;
538
    }
539

    
540
    private Query prepareTaxaByCommonName(String queryString, Classification classification,
541
            MatchMode matchMode, Set<NamedArea> namedAreas, Integer pageSize, Integer pageNumber, boolean doCount, boolean doNotReturnFullEntities){
542

    
543
        String what = "select distinct";
544
        if (doNotReturnFullEntities){
545
        	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 ";
546
        }else {
547
        	what += (doCount ? " count(t)": " t");
548
        }
549
        String hql= what + " from Taxon t " +
550
        "join t.descriptions d "+
551
        "join d.descriptionElements e " +
552
        "join e.feature f " +
553
        "where f.supportsCommonTaxonName = true and e.name "+matchMode.getMatchOperator()+" :queryString";//and ls.text like 'common%'";
554

    
555
        Query query = getSession().createQuery(hql);
556

    
557
        query.setParameter("queryString", matchMode.queryStringFrom(queryString));
558

    
559
        if(pageSize != null &&  !doCount) {
560
            query.setMaxResults(pageSize);
561
            if(pageNumber != null) {
562
                query.setFirstResult(pageNumber * pageSize);
563
            }
564
        }
565
        return query;
566
    }
567

    
568
    @Override
569
    public long countTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, boolean doCommonNames,
570
            boolean doIncludeAuthors, String queryString, Classification classification,
571
        MatchMode matchMode, Set<NamedArea> namedAreas) {
572

    
573
        boolean doCount = true;
574
        /*
575
        boolean doTaxa = true;
576
        boolean doSynonyms = true;
577
        if (clazz.equals(Taxon.class)){
578
            doSynonyms = false;
579
        } else if (clazz.equals(Synonym.class)){
580
            doTaxa = false;
581
        }
582
        */
583
        String searchField = doIncludeAuthors ? "titleCache": "nameCache";
584

    
585
        Query query = prepareTaxaByName(doTaxa, doSynonyms, doMisappliedNames, doCommonNames, searchField, queryString, classification, matchMode, namedAreas, null, null, null, doCount);
586
        if (query != null) {
587
            return (Long)query.uniqueResult();
588
        }else{
589
            return 0;
590
        }
591
    }
592

    
593
    /**
594
     * @param namedAreas
595
     * @param areasExpanded
596
     * @param areaQuery
597
     */
598
    private void expandNamedAreas(Collection<NamedArea> namedAreas, Set<NamedArea> areasExpanded, Query areaQuery) {
599
        List<NamedArea> childAreas;
600
        for(NamedArea a : namedAreas){
601
            areasExpanded.add(a);
602
            areaQuery.setParameter("area", a);
603
            childAreas = areaQuery.list();
604
            if(childAreas.size() > 0){
605
                areasExpanded.addAll(childAreas);
606
                expandNamedAreas(childAreas, areasExpanded, areaQuery);
607
            }
608
        }
609
    }
610

    
611

    
612
    @Override
613
    public List<TaxonBase> getAllTaxonBases(Integer pagesize, Integer page) {
614
        return super.list(pagesize, page);
615
    }
616

    
617
    @Override
618
    public List<Synonym> getAllSynonyms(Integer limit, Integer start) {
619
        Criteria criteria = getSession().createCriteria(Synonym.class);
620

    
621
        if(limit != null) {
622
            criteria.setFirstResult(start);
623
            criteria.setMaxResults(limit);
624
        }
625

    
626
        return criteria.list();
627
    }
628

    
629
    @Override
630
    public List<Taxon> getAllTaxa(Integer limit, Integer start) {
631
        Criteria criteria = getSession().createCriteria(Taxon.class);
632

    
633
        if(limit != null) {
634
            criteria.setFirstResult(start);
635
            criteria.setMaxResults(limit);
636
        }
637

    
638
        return criteria.list();
639
    }
640

    
641

    
642
    @Override
643
    public UUID delete(TaxonBase taxonBase) throws DataAccessException{
644
        if (taxonBase == null){
645
            logger.warn("TaxonBase was 'null'");
646
            return null;
647
        }
648

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

    
656
        taxonBase.removeSources();
657

    
658

    
659
        if (taxonBase instanceof Taxon){ // is Taxon
660
            Taxon taxon = ((Taxon)taxonBase);
661
            Set<Synonym> syns = new HashSet<>(taxon.getSynonyms());
662
            for (Synonym syn: syns){
663
                taxon.removeSynonym(syn);
664
            }
665
        }
666

    
667
       return super.delete(taxonBase);
668

    
669
    }
670

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

    
674
        boolean doCount = false;
675
        Query query = prepareTaxaByName(doTaxa, doSynonyms, false, false, "titleCache", queryString, classification, matchMode, namedAreas, order, pageSize, pageNumber, doCount);
676
        if (query != null){
677
            List<TaxonBase> results = query.list();
678
            defaultBeanInitializer.initializeAll(results, propertyPaths);
679
            return results;
680
        }
681
        return new ArrayList<TaxonBase>();
682

    
683
    }
684

    
685
    @Override
686
    public TaxonBase findByUuid(UUID uuid, List<Criterion> criteria, List<String> propertyPaths) {
687

    
688
        Criteria crit = getSession().createCriteria(type);
689

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

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

    
714
        return null;
715
    }
716

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

    
720
        Criteria crit = getSession().createCriteria(type);
721

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

    
735
        List<? extends TaxonBase> results = crit.list();
736

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

    
741
    @Override
742
    public int countMatchesByName(String queryString, MatchMode matchMode, boolean onlyAcccepted) {
743
        checkNotInPriorView("TaxonDaoHibernateImpl.countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted)");
744
        Criteria crit = getSession().createCriteria(type);
745
        crit.add(Restrictions.ilike("titleCache", matchMode.queryStringFrom(queryString)));
746
        crit.setProjection(Projections.rowCount());
747
        int result = ((Number)crit.list().get(0)).intValue();
748
        return result;
749
    }
750

    
751

    
752
    @Override
753
    public int countMatchesByName(String queryString, MatchMode matchMode, boolean onlyAcccepted, List<Criterion> criteria) {
754
        checkNotInPriorView("TaxonDaoHibernateImpl.countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted, List<Criterion> criteria)");
755
        Criteria crit = getSession().createCriteria(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
        int result = ((Number)crit.list().get(0)).intValue();
764
        return result;
765
    }
766

    
767
    @Override
768
    public int countTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Direction direction) {
769
        AuditEvent auditEvent = getAuditEventFromContext();
770
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
771
            Query query = null;
772

    
773
            if(type == null) {
774
                query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon");
775
            } else {
776
                query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon and taxonRelationship.type = :type");
777
                query.setParameter("type",type);
778
            }
779
            query.setParameter("relatedTaxon", taxon);
780

    
781
            return ((Long)query.uniqueResult()).intValue();
782
        } else {
783
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
784
            query.add(AuditEntity.relatedId(direction.toString()).eq(taxon.getId()));
785
            query.addProjection(AuditEntity.id().count());
786

    
787
            if(type != null) {
788
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
789
            }
790

    
791
            return ((Long)query.getSingleResult()).intValue();
792
        }
793
    }
794

    
795

    
796
    @Override
797
    public int countSynonyms(boolean onlyAttachedToTaxon) {
798
        AuditEvent auditEvent = getAuditEventFromContext();
799
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
800
            Query query = null;
801

    
802
            String queryStr = "SELECT count(syn) FROM Synonym syn";
803
            if (onlyAttachedToTaxon){
804
                queryStr += " WHERE syn.acceptedTaxon IS NOT NULL";
805
            }
806
            query = getSession().createQuery(queryStr);
807

    
808
            return ((Long)query.uniqueResult()).intValue();
809
        } else {
810
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(Synonym.class,auditEvent.getRevisionNumber());
811
            if (onlyAttachedToTaxon){
812
                query.add(new NotNullAuditExpression(new EntityPropertyName("acceptedTaxon")));
813
            }
814
            query.addProjection(AuditEntity.id().count());
815

    
816
            return ((Long)query.getSingleResult()).intValue();
817
        }
818
    }
819

    
820
    @Override
821
    public long countSynonyms(Taxon taxon, SynonymType type) {
822
        AuditEvent auditEvent = getAuditEventFromContext();
823
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
824
            Criteria criteria = getSession().createCriteria(Synonym.class);
825

    
826
            criteria.add(Restrictions.eq("acceptedTaxon", taxon));
827
            if(type != null) {
828
                criteria.add(Restrictions.eq("type", type));
829
            }
830
            criteria.setProjection(Projections.rowCount());
831
            return ((Number)criteria.uniqueResult()).intValue();
832
        } else {
833
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(Synonym.class,auditEvent.getRevisionNumber());
834
            query.add(AuditEntity.relatedId("acceptedTaxon").eq(taxon.getId()));
835
            query.addProjection(AuditEntity.id().count());
836

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

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

    
845
    @Override
846
    public int countSynonyms(Synonym synonym, SynonymType type) {
847
        AuditEvent auditEvent = getAuditEventFromContext();
848
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
849
            Criteria criteria = getSession().createCriteria(Synonym.class);
850

    
851
            criteria.add(Restrictions.isNotNull("acceptedTaxon"));
852
            if(type != null) {
853
                criteria.add(Restrictions.eq("type", type));
854
            }
855

    
856
            criteria.setProjection(Projections.rowCount());
857
            return ((Number)criteria.uniqueResult()).intValue();
858
        } else {
859
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(Synonym.class,auditEvent.getRevisionNumber());
860
            query.add(new NotNullAuditExpression(new EntityPropertyName("acceptedTaxon")));
861
            query.addProjection(AuditEntity.id().count());
862

    
863
            if(type != null) {
864
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
865
            }
866

    
867
            return ((Long)query.getSingleResult()).intValue();
868
        }
869
    }
870

    
871
    @Override
872
    public int countTaxaByName(Class<? extends TaxonBase> clazz, String genusOrUninomial, String infraGenericEpithet, String specificEpithet,	String infraSpecificEpithet, Rank rank) {
873
        checkNotInPriorView("TaxonDaoHibernateImpl.countTaxaByName(Boolean accepted, String genusOrUninomial,	String infraGenericEpithet, String specificEpithet,	String infraSpecificEpithet, Rank rank)");
874
        Criteria criteria = null;
875

    
876
        criteria = getSession().createCriteria(clazz);
877

    
878
        criteria.setFetchMode( "name", FetchMode.JOIN );
879
        criteria.createAlias("name", "name");
880

    
881
        if(genusOrUninomial == null) {
882
            criteria.add(Restrictions.isNull("name.genusOrUninomial"));
883
        } else if(!genusOrUninomial.equals("*")) {
884
            criteria.add(Restrictions.eq("name.genusOrUninomial", genusOrUninomial));
885
        }
886

    
887
        if(infraGenericEpithet == null) {
888
            criteria.add(Restrictions.isNull("name.infraGenericEpithet"));
889
        } else if(!infraGenericEpithet.equals("*")) {
890
            criteria.add(Restrictions.eq("name.infraGenericEpithet", infraGenericEpithet));
891
        }
892

    
893
        if(specificEpithet == null) {
894
            criteria.add(Restrictions.isNull("name.specificEpithet"));
895
        } else if(!specificEpithet.equals("*")) {
896
            criteria.add(Restrictions.eq("name.specificEpithet", specificEpithet));
897

    
898
        }
899

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

    
906
        if(rank != null) {
907
            criteria.add(Restrictions.eq("name.rank", rank));
908
        }
909

    
910
        criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
911

    
912
        return ((Number)criteria.uniqueResult()).intValue();
913
    }
914

    
915
    @Override
916
    public List<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, String authorship, Rank rank, Integer pageSize,	Integer pageNumber) {
917
        checkNotInPriorView("TaxonDaoHibernateImpl.findTaxaByName(Boolean accepted, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, String authorship, Rank rank, Integer pageSize,	Integer pageNumber)");
918
        Criteria criteria = null;
919
        if (clazz == null){
920
            criteria = getSession().createCriteria(TaxonBase.class);
921
        } else{
922
            criteria = getSession().createCriteria(clazz);
923
        }
924
        criteria.setFetchMode( "name", FetchMode.JOIN );
925
        criteria.createAlias("name", "name");
926

    
927
        if(genusOrUninomial == null) {
928
            criteria.add(Restrictions.isNull("name.genusOrUninomial"));
929
        } else if(!genusOrUninomial.equals("*")) {
930
            criteria.add(Restrictions.eq("name.genusOrUninomial", genusOrUninomial));
931
        }
932

    
933
        if(infraGenericEpithet == null) {
934
            criteria.add(Restrictions.isNull("name.infraGenericEpithet"));
935
        } else if(!infraGenericEpithet.equals("*")) {
936
            criteria.add(Restrictions.eq("name.infraGenericEpithet", infraGenericEpithet));
937
        }
938

    
939
        if(specificEpithet == null) {
940
            criteria.add(Restrictions.isNull("name.specificEpithet"));
941
        } else if(!specificEpithet.equals("*")) {
942
            criteria.add(Restrictions.eq("name.specificEpithet", specificEpithet));
943

    
944
        }
945

    
946
        if(infraSpecificEpithet == null) {
947
            criteria.add(Restrictions.isNull("name.infraSpecificEpithet"));
948
        } else if(!infraSpecificEpithet.equals("*")) {
949
            criteria.add(Restrictions.eq("name.infraSpecificEpithet", infraSpecificEpithet));
950
        }
951

    
952
        if(authorship == null) {
953
            criteria.add(Restrictions.eq("name.authorshipCache", ""));
954
        } else if(!authorship.equals("*")) {
955
            criteria.add(Restrictions.eq("name.authorshipCache", authorship));
956
        }
957

    
958
        if(rank != null) {
959
            criteria.add(Restrictions.eq("name.rank", rank));
960
        }
961

    
962
        if(pageSize != null) {
963
            criteria.setMaxResults(pageSize);
964
            if(pageNumber != null) {
965
                criteria.setFirstResult(pageNumber * pageSize);
966
            } else {
967
                criteria.setFirstResult(0);
968
            }
969
        }
970

    
971
        return criteria.list();
972
    }
973

    
974
    @Override
975
    public List<TaxonRelationship> getTaxonRelationships(Taxon taxon, TaxonRelationshipType type,
976
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
977
            List<String> propertyPaths, Direction direction) {
978

    
979
        AuditEvent auditEvent = getAuditEventFromContext();
980
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
981

    
982
            Criteria criteria = getSession().createCriteria(TaxonRelationship.class);
983

    
984
            if(direction != null) {
985
                criteria.add(Restrictions.eq(direction.name(), taxon));
986
            } else {
987
                criteria.add(Restrictions.or(
988
                        Restrictions.eq(Direction.relatedFrom.name(), taxon),
989
                        Restrictions.eq(Direction.relatedTo.name(), taxon))
990
                    );
991
            }
992

    
993
            if(type != null) {
994
                criteria.add(Restrictions.eq("type", type));
995
            }
996

    
997
            addOrder(criteria,orderHints);
998

    
999
            if(pageSize != null) {
1000
                criteria.setMaxResults(pageSize);
1001
                if(pageNumber != null) {
1002
                    criteria.setFirstResult(pageNumber * pageSize);
1003
                } else {
1004
                    criteria.setFirstResult(0);
1005
                }
1006
            }
1007

    
1008
            @SuppressWarnings("unchecked")
1009
            List<TaxonRelationship> result = criteria.list();
1010
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1011

    
1012
            return result;
1013
        } else {
1014
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1015
            query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));
1016

    
1017
            if(type != null) {
1018
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1019
            }
1020

    
1021
            if(pageSize != null) {
1022
                query.setMaxResults(pageSize);
1023
                if(pageNumber != null) {
1024
                    query.setFirstResult(pageNumber * pageSize);
1025
                } else {
1026
                    query.setFirstResult(0);
1027
                }
1028
            }
1029

    
1030
            List<TaxonRelationship> result = query.getResultList();
1031
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1032

    
1033
            // Ugly, but for now, there is no way to sort on a related entity property in Envers,
1034
            // and we can't live without this functionality in CATE as it screws up the whole
1035
            // taxon tree thing
1036
            if(orderHints != null && !orderHints.isEmpty()) {
1037
                SortedSet<TaxonRelationship> sortedList = new TreeSet<TaxonRelationship>(new TaxonRelationshipFromTaxonComparator());
1038
                sortedList.addAll(result);
1039
                return new ArrayList<TaxonRelationship>(sortedList);
1040
            }
1041

    
1042
            return result;
1043
        }
1044
    }
1045

    
1046
    class TaxonRelationshipFromTaxonComparator implements Comparator<TaxonRelationship> {
1047

    
1048
        @Override
1049
        public int compare(TaxonRelationship o1, TaxonRelationship o2) {
1050
            if (o1.equals(o2)){
1051
                return 0;
1052
            }
1053
            int result = o1.getFromTaxon().getTitleCache().compareTo(o2.getFromTaxon().getTitleCache());
1054
            if (result == 0 ){
1055
                result = o1.getUuid().compareTo(o2.getUuid());
1056
            }
1057
            return result;
1058
        }
1059

    
1060
    }
1061

    
1062
    @Override
1063
    public List<Synonym> getSynonyms(Taxon taxon, SynonymType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
1064
        AuditEvent auditEvent = getAuditEventFromContext();
1065
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1066
            Criteria criteria = getSession().createCriteria(Synonym.class);
1067

    
1068
            criteria.add(Restrictions.eq("acceptedTaxon", taxon));
1069
            if(type != null) {
1070
                criteria.add(Restrictions.eq("type", type));
1071
            }
1072

    
1073
            addOrder(criteria,orderHints);
1074

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

    
1084
            @SuppressWarnings("unchecked")
1085
            List<Synonym> result = criteria.list();
1086
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1087

    
1088
            return result;
1089
        } else {
1090
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(Synonym.class,auditEvent.getRevisionNumber());
1091
            query.add(AuditEntity.relatedId("acceptedTaxon").eq(taxon.getId()));
1092

    
1093
            if(type != null) {
1094
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1095
            }
1096

    
1097
            if(pageSize != null) {
1098
                query.setMaxResults(pageSize);
1099
                if(pageNumber != null) {
1100
                    query.setFirstResult(pageNumber * pageSize);
1101
                } else {
1102
                    query.setFirstResult(0);
1103
                }
1104
            }
1105

    
1106
            List<Synonym> result = query.getResultList();
1107
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1108

    
1109
            return result;
1110
        }
1111
    }
1112

    
1113
    @Override
1114
    public void rebuildIndex() {
1115
        FullTextSession fullTextSession = Search.getFullTextSession(getSession());
1116

    
1117
        for(TaxonBase taxonBase : list(null,null)) { // re-index all taxon base
1118
            Hibernate.initialize(taxonBase.getName());
1119
            fullTextSession.index(taxonBase);
1120
        }
1121
        fullTextSession.flushToIndexes();
1122
    }
1123

    
1124
    @Override
1125
    public String suggestQuery(String queryString) {
1126
        throw new RuntimeException("Query suggestion currently not implemented in TaxonDaoHibernateImpl");
1127
//        checkNotInPriorView("TaxonDaoHibernateImpl.suggestQuery(String queryString)");
1128
//        String alternativeQueryString = null;
1129
//        if (alternativeSpellingSuggestionParser != null) {
1130
//            try {
1131
//
1132
//                alternativeSpellingSuggestionParser.parse(queryString);
1133
//                org.apache.lucene.search.Query alternativeQuery = alternativeSpellingSuggestionParser.suggest(queryString);
1134
//                if (alternativeQuery != null) {
1135
//                    alternativeQueryString = alternativeQuery
1136
//                            .toString("name.titleCache");
1137
//                }
1138
//
1139
//            } catch (ParseException e) {
1140
//                throw new QueryParseException(e, queryString);
1141
//            }
1142
//        }
1143
//        return alternativeQueryString;
1144
    }
1145

    
1146
    @Override
1147
    public Taxon acceptedTaxonFor(Synonym synonym, Classification classificationFilter, List<String> propertyPaths){
1148

    
1149
        String hql = prepareListAcceptedTaxaFor(classificationFilter, false);
1150

    
1151
        Query query = getSession().createQuery(hql);
1152

    
1153
        query.setParameter("synonym", synonym);
1154

    
1155
        if(classificationFilter != null){
1156
            query.setParameter("classificationFilter", classificationFilter);
1157
        }
1158

    
1159
        @SuppressWarnings("unchecked")
1160
        List<Taxon> result = query.list();
1161

    
1162
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1163

    
1164
        return result.isEmpty()? null: result.get(0);
1165
    }
1166

    
1167
    @Override
1168
    public long countAcceptedTaxonFor(Synonym synonym, Classification classificationFilter){
1169

    
1170
        String hql = prepareListAcceptedTaxaFor(classificationFilter, true);
1171

    
1172
        Query query = getSession().createQuery(hql);
1173

    
1174
        query.setParameter("synonym", synonym);
1175

    
1176
        if(classificationFilter != null){
1177
            query.setParameter("classificationFilter", classificationFilter);
1178
        }
1179

    
1180
        Long count = Long.parseLong(query.uniqueResult().toString());
1181

    
1182
        return count;
1183

    
1184
    }
1185

    
1186

    
1187
    /**
1188
     * @param classificationFilter
1189
     * @param orderHints
1190
     * @return
1191
     */
1192
    private String prepareListAcceptedTaxaFor(Classification classificationFilter, boolean doCount) {
1193

    
1194
        String hql;
1195
        String hqlSelect = "SELECT " + (doCount? "COUNT(taxon)" : "taxon") + " FROM Synonym as syn JOIN syn.acceptedTaxon as taxon ";
1196
        String hqlWhere = " WHERE syn = :synonym";
1197

    
1198
        if(classificationFilter != null){
1199
            hqlSelect += " JOIN taxon.taxonNodes AS taxonNode";
1200
            hqlWhere += " AND taxonNode.classification = :classificationFilter";
1201
        }
1202
        hql = hqlSelect + hqlWhere;
1203
        return hql;
1204
    }
1205

    
1206
    @Override
1207
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, Integer limit, String pattern) {
1208
        int classificationId = classification.getId();
1209
        // StringBuffer excludeUuids = new StringBuffer();
1210

    
1211
         String queryString = "SELECT nodes.uuid, nodes.id, taxon.titleCache, taxon.name.rank FROM TaxonNode AS nodes JOIN nodes.taxon as taxon WHERE nodes.classification.id = " + classificationId ;
1212
         if (pattern != null){
1213
             if (pattern.equals("?")){
1214
                 limit = null;
1215
             } else{
1216
                 if (!pattern.endsWith("*")){
1217
                     pattern += "%";
1218
                 }
1219
                 pattern = pattern.replace("*", "%");
1220
                 pattern = pattern.replace("?", "%");
1221
                 queryString = queryString + " AND taxon.titleCache like (:pattern)" ;
1222
             }
1223
         }
1224

    
1225

    
1226

    
1227
         Query query = getSession().createQuery(queryString);
1228

    
1229

    
1230
         if (limit != null){
1231
             query.setMaxResults(limit);
1232
         }
1233

    
1234
         if (pattern != null && !pattern.equals("?")){
1235
             query.setParameter("pattern", pattern);
1236
         }
1237
         @SuppressWarnings("unchecked")
1238
         List<Object[]> result = query.list();
1239

    
1240
         if(result.size() == 0){
1241
             return null;
1242
         }else{
1243
             List<UuidAndTitleCache<TaxonNode>> list = new ArrayList<UuidAndTitleCache<TaxonNode>>(result.size());
1244
             if (result != null && !result.isEmpty()){
1245
                 if (result.iterator().next().length == 4){
1246
                     Collections.sort(result, new UuidAndTitleCacheTaxonComparator());
1247
                 }
1248
             }
1249
             for (Object object : result){
1250

    
1251
                 Object[] objectArray = (Object[]) object;
1252

    
1253
                 UUID uuid = (UUID)objectArray[0];
1254
                 Integer id = (Integer) objectArray[1];
1255
                 String titleCache = (String) objectArray[2];
1256

    
1257
                 list.add(new UuidAndTitleCache<TaxonNode>(TaxonNode.class, uuid, id, titleCache));
1258
             }
1259

    
1260
             return list;
1261
         }
1262
    }
1263

    
1264

    
1265
    @Override
1266
    public TaxonBase find(LSID lsid) {
1267
        TaxonBase<?> taxonBase = super.find(lsid);
1268
        if(taxonBase != null) {
1269
            List<String> propertyPaths = new ArrayList<String>();
1270
            propertyPaths.add("createdBy");
1271
            propertyPaths.add("updatedBy");
1272
            propertyPaths.add("name");
1273
            propertyPaths.add("sec");
1274
            propertyPaths.add("relationsToThisTaxon");
1275
            propertyPaths.add("relationsToThisTaxon.fromTaxon");
1276
            propertyPaths.add("relationsToThisTaxon.toTaxon");
1277
            propertyPaths.add("relationsFromThisTaxon");
1278
            propertyPaths.add("relationsFromThisTaxon.toTaxon");
1279
            propertyPaths.add("relationsToThisTaxon.type");
1280
            propertyPaths.add("synonyms");
1281
            propertyPaths.add("synonyms.type");
1282
            propertyPaths.add("descriptions");
1283

    
1284
            defaultBeanInitializer.initialize(taxonBase, propertyPaths);
1285
        }
1286
        return taxonBase;
1287
    }
1288

    
1289

    
1290

    
1291
    @Override
1292
    public List<String> taxaByNameNotInDB(List<String> taxonNames){
1293
        //get all taxa, already in db
1294
        Query query = getSession().createQuery("from TaxonName t where t.nameCache IN (:taxonList)");
1295
        query.setParameterList("taxonList", taxonNames);
1296
        List<TaxonName> taxaInDB = query.list();
1297
        //compare the original list with the result of the query
1298
        for (TaxonName taxonName: taxaInDB){
1299
            String nameCache = taxonName.getNameCache();
1300
            if (taxonNames.contains(nameCache)){
1301
                taxonNames.remove(nameCache);
1302
            }
1303

    
1304
        }
1305

    
1306
        return taxonNames;
1307
    }
1308

    
1309
    //TODO: mal nur mit UUID probieren (ohne fetch all properties), vielleicht geht das schneller?
1310
    @Override
1311
    public List<UUID> findIdenticalTaxonNameIds(List<String> propertyPaths){
1312
        Query query=getSession().createQuery("select tmb2 from ZoologicalName tmb, ZoologicalName tmb2 fetch all properties where tmb.id != tmb2.id and tmb.nameCache = tmb2.nameCache");
1313
        List<UUID> zooNames = query.list();
1314

    
1315
        return zooNames;
1316

    
1317
    }
1318

    
1319
    @Override
1320
    public List<TaxonName> findIdenticalTaxonNames(List<String> propertyPaths) {
1321

    
1322
        Query query=getSession().createQuery("select tmb2 from ZoologicalName tmb, ZoologicalName tmb2 fetch all properties where tmb.id != tmb2.id and tmb.nameCache = tmb2.nameCache");
1323

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

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

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

    
1334
        return zooNames;
1335
    }
1336

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

    
1340
        //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)
1341
        Query query = getSession().createQuery("Select id from Reference where titleCache like 'Fauna Europaea database'");
1342
        List<String> secRefFauna = query.list();
1343
        query = getSession().createQuery("Select id from Reference where titleCache like 'ERMS'");
1344
        List<String> secRefErms = query.list();
1345
        //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");
1346
        //Get all names of fauna europaea
1347
        query = getSession().createQuery("select zn.nameCache from ZoologicalName zn, TaxonBase tb where tb.name = zn and tb.sec.id = :secRefFauna");
1348
        query.setParameter("secRefFauna", secRefFauna.get(0));
1349
        List<String> namesFauna= query.list();
1350

    
1351
        //Get all names of erms
1352

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

    
1356
        List<String> namesErms = query.list();
1357
        /*TaxonNameComparator comp = new TaxonNameComparator();
1358
        Collections.sort(namesFauna);
1359
        Collections.sort(namesErms);
1360
        */
1361
        List <String> identicalNames = new ArrayList<String>();
1362
        String predecessor = "";
1363

    
1364
        for (String nameFauna: namesFauna){
1365
            if (namesErms.contains(nameFauna)){
1366
                identicalNames.add(nameFauna);
1367
            }
1368
        }
1369

    
1370

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

    
1376
        Iterator<IdentifiableSource> sources = tempName.getSources().iterator();
1377

    
1378
        TaxonNameComparator taxComp = new TaxonNameComparator();
1379
        Collections.sort(result, taxComp);
1380
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1381
        return result;
1382

    
1383
    }
1384

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

    
1401

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

    
1417
    private String[] createHQLString(boolean doTaxa, boolean doSynonyms, boolean doIncludeMisappliedNames, Classification classification,  Set<NamedArea> areasExpanded, MatchMode matchMode, String searchField){
1418

    
1419
           boolean doAreaRestriction = areasExpanded.size() > 0;
1420
           String 	doAreaRestrictionSubSelect = "select %s.id from" +
1421
                " Distribution e" +
1422
                " join e.inDescription d" +
1423
                " join d.taxon t" +
1424
                (classification != null ? " join t.taxonNodes as tn " : " ");
1425

    
1426
           String 	doAreaRestrictionMisappliedNameSubSelect = "select %s.id from" +
1427
            " Distribution e" +
1428
            " join e.inDescription d" +
1429
            " join d.taxon t";
1430

    
1431
           String doTaxonSubSelect = "select %s.id from Taxon t " + (classification != null ? " join t.taxonNodes as tn " : " ");
1432
           String doTaxonMisappliedNameSubSelect = "select %s.id from Taxon t ";
1433

    
1434
           String doTaxonNameJoin =   " join t.name n ";
1435

    
1436
           String doSynonymNameJoin =  	" join t.synonyms s join s.name sn";
1437

    
1438
           String doMisappliedNamesJoin = " left join t.relationsFromThisTaxon as rft" +
1439
                " left join rft.relatedTo as rt" +
1440
                (classification != null ? " left join rt.taxonNodes as tn2" : " ") +
1441
                " left join rt.name as n2" +
1442
                " left join rft.type as rtype";
1443

    
1444
           String doCommonNamesJoin =   "join t.descriptions as description "+
1445
                   "left join description.descriptionElements as com " +
1446
                   "left join com.feature f ";
1447

    
1448

    
1449
           String doClassificationWhere = " tn.classification = :classification";
1450
           String doClassificationForMisappliedNamesWhere = " tn2 .classification = :classification";
1451

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

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

    
1457
           String doRelationshipTypeComparison = " rtype in (:rTypeSet) ";
1458

    
1459
        String taxonSubselect = null;
1460
        String synonymSubselect = null;
1461
        String misappliedSelect = null;
1462
        String commonNameSubselect = null;
1463

    
1464
        if(classification != null ){
1465
            if (!doIncludeMisappliedNames){
1466
                if(doAreaRestriction){
1467
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
1468
                    " WHERE " + doAreaRestrictionWhere +
1469
                    " AND " + doClassificationWhere +
1470
                    " AND " + String.format(doSearchFieldWhere, "n");
1471
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
1472
                    " WHERE " + doAreaRestrictionWhere +
1473
                    " AND " + doClassificationWhere +
1474
                    " AND " + String.format(doSearchFieldWhere, "sn");
1475
                    commonNameSubselect =  String.format(doAreaRestrictionSubSelect, "t") + doCommonNamesJoin +
1476
                            " WHERE " +  doAreaRestrictionWhere + " AND " + doClassificationWhere +
1477
                            " AND " + String.format(doSearchFieldWhere, "n")
1478
                            + " AND " + doCommonNamesRestrictionWhere;
1479
                } else {
1480
                    taxonSubselect = String.format(doTaxonSubSelect, "t" )+ doTaxonNameJoin +
1481
                    " WHERE " + doClassificationWhere +
1482
                    " AND " + String.format(doSearchFieldWhere, "n");
1483
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
1484
                    " WHERE " + doClassificationWhere +
1485
                    " AND " + String.format(doSearchFieldWhere, "sn");
1486
                    commonNameSubselect =String.format(doTaxonSubSelect, "t" )+ doCommonNamesJoin +
1487
                            " WHERE " + doClassificationWhere +
1488
                            " AND " + doCommonNamesRestrictionWhere;
1489
                }
1490
            }else{ //misappliedNames included
1491
                if(doAreaRestriction){
1492
                    misappliedSelect = String.format(doAreaRestrictionMisappliedNameSubSelect, "t") + doTaxonNameJoin + doMisappliedNamesJoin  +
1493
                    " WHERE " + doAreaRestrictionWhere +
1494
                    " AND " + String.format(doSearchFieldWhere, "n") +
1495
                    " AND " + doClassificationForMisappliedNamesWhere +
1496
                    " AND " + doRelationshipTypeComparison;
1497

    
1498
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
1499
                    " WHERE " + doAreaRestrictionWhere +
1500
                    " AND "+ String.format(doSearchFieldWhere, "n") + " AND "+ doClassificationWhere;
1501

    
1502
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
1503
                    " WHERE " + doAreaRestrictionWhere +
1504
                    " AND " + doClassificationWhere + " AND " +  String.format(doSearchFieldWhere, "sn");
1505

    
1506
                    commonNameSubselect= String.format(doAreaRestrictionSubSelect, "t")+ doCommonNamesJoin +
1507
                            " WHERE " + doAreaRestrictionWhere +
1508
                            " AND "+ doClassificationWhere + " AND " + doCommonNamesRestrictionWhere;
1509
                } else {
1510
                    misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin +
1511
                    " WHERE " + String.format(doSearchFieldWhere, "n") +
1512
                    " AND " + doClassificationForMisappliedNamesWhere +
1513
                    " AND " + doRelationshipTypeComparison;
1514

    
1515
                    taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin +
1516
                    " WHERE " +  String.format(doSearchFieldWhere, "n") +
1517
                    " AND "+ doClassificationWhere;
1518

    
1519
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
1520
                    " WHERE " + doClassificationWhere +
1521
                    " AND " +  String.format(doSearchFieldWhere, "sn");
1522

    
1523
                    commonNameSubselect= String.format(doTaxonSubSelect, "t")+ doCommonNamesJoin +
1524
                            " WHERE " + doClassificationWhere + " AND " + doCommonNamesRestrictionWhere;
1525

    
1526
                }
1527
            }
1528
        } else {
1529
            if(doAreaRestriction){
1530
                misappliedSelect = String.format(doAreaRestrictionMisappliedNameSubSelect, "t") + doTaxonNameJoin + doMisappliedNamesJoin +
1531
                " WHERE " + doAreaRestrictionWhere +
1532
                " AND " + String.format(doSearchFieldWhere, "n")+
1533
                " AND " + doRelationshipTypeComparison;
1534

    
1535
                taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
1536
                " WHERE " + doAreaRestrictionWhere +
1537
                " AND " + String.format(doSearchFieldWhere, "n");
1538

    
1539
                synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
1540
                " WHERE " +   doAreaRestrictionWhere +
1541
                " AND " +  String.format(doSearchFieldWhere, "sn");
1542
                commonNameSubselect = String.format(doAreaRestrictionSubSelect, "t")+ doCommonNamesJoin +
1543
                        " WHERE " + doAreaRestrictionWhere +
1544
                        " AND " + doCommonNamesRestrictionWhere;
1545

    
1546

    
1547
            } else {
1548
                misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin + " WHERE " +  String.format(doSearchFieldWhere, "n") + " AND " + doRelationshipTypeComparison;
1549
                taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "n");
1550
                synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "sn");
1551
                commonNameSubselect = String.format(doTaxonSubSelect, "t" ) +doCommonNamesJoin + " WHERE "+  doCommonNamesRestrictionWhere;
1552

    
1553
            }
1554
        }
1555
        String[] result = {misappliedSelect, taxonSubselect, synonymSubselect, commonNameSubselect};
1556

    
1557
        return result;
1558
    }
1559

    
1560
	@Override
1561
	public List<UuidAndTitleCache<IdentifiableEntity>> getTaxaByCommonNameForEditor(
1562
			String titleSearchStringSqlized, Classification classification,
1563
			MatchMode matchMode, Set namedAreas) {
1564
	    List<Object> resultArray = new ArrayList<Object>();
1565
		Query query = prepareTaxaByCommonName(titleSearchStringSqlized, classification, matchMode, namedAreas, null, null, false, true);
1566
        if (query != null){
1567
            resultArray = query.list();
1568
            List<UuidAndTitleCache<IdentifiableEntity>> returnResult = new ArrayList<UuidAndTitleCache<IdentifiableEntity>>() ;
1569
            Object[] result;
1570
            for(int i = 0; i<resultArray.size();i++){
1571
            	result = (Object[]) resultArray.get(i);
1572
            	returnResult.add(new UuidAndTitleCache(Taxon.class, (UUID) result[0],(Integer)result[1], (String)result[2], new Boolean(result[4].toString()), null));
1573
            }
1574
            return returnResult;
1575
        }
1576

    
1577
		return null;
1578
	}
1579

    
1580

    
1581
	/**
1582
	 * @param
1583
	 * @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)
1584
	 */
1585
	@Override
1586
	public <S extends TaxonBase> int countByIdentifier(Class<S> clazz,
1587
			String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter, MatchMode matchmode) {
1588
		if (subtreeFilter == null){
1589
			return countByIdentifier(clazz, identifier, identifierType, matchmode);
1590
		}
1591

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

    
1595
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
1596
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
1597

    
1598
		getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
1599
		String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
1600
		String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
1601
		String synTreeJoin = isSynonym ? " LEFT JOIN c.acceptedTaxon as acc LEFT JOIN acc.taxonNodes synTn  " : "";
1602
		String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
1603
		String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
1604

    
1605
		String queryString = "SELECT count(*)  FROM %s as c " +
1606
                " INNER JOIN c.identifiers as ids " +
1607
                accTreeJoin +
1608
                synTreeJoin +
1609
                " WHERE (1=1) " +
1610
                	"  AND ( " + accWhere + " OR " + synWhere + ")";
1611
		queryString = String.format(queryString, clazzParam.getSimpleName());
1612

    
1613
		if (identifier != null){
1614
			if (matchmode == null || matchmode == MatchMode.EXACT){
1615
				queryString += " AND ids.identifier = '"  + identifier + "'";
1616
			}else {
1617
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
1618
			}
1619
		}
1620
		if (identifierType != null){
1621
        	queryString += " AND ids.type = :type";
1622
        }
1623

    
1624
		Query query = getSession().createQuery(queryString);
1625
        if (identifierType != null){
1626
        	query.setEntity("type", identifierType);
1627
        }
1628

    
1629
		Long c = (Long)query.uniqueResult();
1630
        return c.intValue();
1631
	}
1632

    
1633
	@Override
1634
	public <S extends TaxonBase> List<Object[]> findByIdentifier(
1635
			Class<S> clazz, String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter,
1636
			MatchMode matchmode, boolean includeEntity,
1637
			Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
1638

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

    
1642
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
1643
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
1644
		getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
1645
		String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
1646
		String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
1647
		String synTreeJoin = isSynonym ? " LEFT JOIN c.acceptedTaxon as acc LEFT JOIN acc.taxonNodes synTn  " : "";
1648
		String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
1649
		String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
1650

    
1651
		String queryString = "SELECT ids.type, ids.identifier, %s " +
1652
				" FROM %s as c " +
1653
                " INNER JOIN c.identifiers as ids " +
1654
                accTreeJoin +
1655
				synTreeJoin +
1656
                " WHERE (1=1) " +
1657
                	" AND ( " + accWhere + " OR " + synWhere + ")";
1658
		queryString = String.format(queryString, (includeEntity ? "c":"c.uuid, c.titleCache") , clazzParam.getSimpleName());
1659

    
1660
		//Matchmode and identifier
1661
		if (identifier != null){
1662
			if (matchmode == null || matchmode == MatchMode.EXACT){
1663
				queryString += " AND ids.identifier = '"  + identifier + "'";
1664
			}else {
1665
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
1666
			}
1667
		}
1668
        if (identifierType != null){
1669
        	queryString += " AND ids.type = :type";
1670
        }
1671
        //order
1672
        queryString +=" ORDER BY ids.type.uuid, ids.identifier, c.uuid ";
1673

    
1674
		Query query = getSession().createQuery(queryString);
1675

    
1676
		//parameters
1677
		if (identifierType != null){
1678
        	query.setEntity("type", identifierType);
1679
        }
1680

    
1681
        //paging
1682
        setPagingParameter(query, pageSize, pageNumber);
1683

    
1684
        List<Object[]> results = query.list();
1685
        //initialize
1686
        if (includeEntity){
1687
        	List<S> entities = new ArrayList<S>();
1688
        	for (Object[] result : results){
1689
        		entities.add((S)result[2]);
1690
        	}
1691
        	defaultBeanInitializer.initializeAll(entities, propertyPaths);
1692
        }
1693
        return results;
1694
	}
1695

    
1696
    /**
1697
     * {@inheritDoc}
1698
     * @see #countByIdentifier(Class, String, DefinedTerm, TaxonNode, MatchMode)
1699
     */
1700
    @Override
1701
    public <S extends TaxonBase> long countByMarker(Class<S> clazz, MarkerType markerType,
1702
            Boolean markerValue, TaxonNode subtreeFilter) {
1703
        if (markerType == null){
1704
            return 0;
1705
        }
1706

    
1707
        if (subtreeFilter == null){
1708
            return countByMarker(clazz, markerType, markerValue);
1709
        }
1710

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

    
1714
        boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
1715
        boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
1716

    
1717
        getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
1718
        String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
1719
        String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
1720
        String synTreeJoin = isSynonym ? " LEFT JOIN c.acceptedTaxon acc LEFT JOIN acc.taxonNodes synTn  " : "";
1721
        String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
1722
        String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
1723

    
1724
        String queryString = "SELECT count(*)  FROM %s as c " +
1725
                " INNER JOIN c.markers as mks " +
1726
                accTreeJoin +
1727
                synTreeJoin +
1728
                " WHERE (1=1) " +
1729
                    "  AND ( " + accWhere + " OR " + synWhere + ")";
1730
        queryString = String.format(queryString, clazzParam.getSimpleName());
1731

    
1732
        if (markerValue != null){
1733
            queryString += " AND mks.flag = :flag";
1734
        }
1735
        if (markerType != null){
1736
            queryString += " AND mks.markerType = :type";
1737
        }
1738

    
1739
        Query query = getSession().createQuery(queryString);
1740
        if (markerType != null){
1741
            query.setEntity("type", markerType);
1742
        }
1743
        if (markerValue != null){
1744
            query.setBoolean("flag", markerValue);
1745
        }
1746

    
1747
        Long c = (Long)query.uniqueResult();
1748
        return c;
1749
    }
1750

    
1751
    /**
1752
     * {@inheritDoc}
1753
     */
1754
    @Override
1755
    public <S extends TaxonBase> List<Object[]> findByMarker(Class<S> clazz, MarkerType markerType,
1756
            Boolean markerValue, TaxonNode subtreeFilter, boolean includeEntity,
1757
            TaxonTitleType titleType, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
1758
        checkNotInPriorView("TaxonDaoHibernateImpl.findByMarker(T clazz, String identifier, DefinedTerm identifierType, MatchMode matchmode, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)");
1759
        if (markerType == null){
1760
            return new ArrayList<Object[]>();
1761
        }
1762
        if (titleType == null){
1763
            titleType = TaxonTitleType.DEFAULT();
1764
        }
1765

    
1766
        Class<?> clazzParam = clazz == null ? type : clazz;
1767

    
1768
        boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
1769
        boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
1770
        getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
1771
        String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
1772
        String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
1773
        String synTreeJoin = isSynonym ? " LEFT JOIN c.acceptedTaxon as acc LEFT JOIN acc.taxonNodes synTn  " : "";
1774
        String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
1775
        String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
1776
        String selectParams = includeEntity ? "c" : titleType.hqlReplaceSelect("c.uuid, c.titleCache", "c.titleCache");
1777
        String titleTypeJoin = includeEntity ? "" : titleType.hqlJoin();
1778

    
1779
        String queryString = "SELECT mks.markerType, mks.flag, %s " +
1780
                " FROM %s as c " +
1781
                " INNER JOIN c.markers as mks " +
1782
                titleTypeJoin +
1783
                accTreeJoin +
1784
                synTreeJoin +
1785
                " WHERE (1=1) " +
1786
                    " AND ( " + accWhere + " OR " + synWhere + ")";
1787
        queryString = String.format(queryString, selectParams, clazzParam.getSimpleName());
1788

    
1789
        //type and value
1790
        if (markerValue != null){
1791
            queryString += " AND mks.flag = :flag";
1792
        }
1793
        queryString += " AND mks.markerType = :type";
1794
        //order
1795
        queryString +=" ORDER BY mks.markerType.uuid, mks.flag, c.uuid ";
1796

    
1797
        Query query = getSession().createQuery(queryString);
1798

    
1799
        //parameters
1800
        query.setEntity("type", markerType);
1801
        if (markerValue != null){
1802
            query.setBoolean("flag", markerValue);
1803
        }
1804

    
1805
        //paging
1806
        setPagingParameter(query, pageSize, pageNumber);
1807

    
1808
        List<Object[]> results = query.list();
1809
        //initialize
1810
        if (includeEntity){
1811
            List<S> entities = new ArrayList<S>();
1812
            for (Object[] result : results){
1813
                entities.add((S)result[2]);
1814
            }
1815
            defaultBeanInitializer.initializeAll(entities, propertyPaths);
1816
        }
1817
        return results;
1818
    }
1819

    
1820
    @Override
1821
    public long countTaxonRelationships(Set<TaxonRelationshipType> types) {
1822
        Criteria criteria = getSession().createCriteria(TaxonRelationship.class);
1823

    
1824
        if (types != null) {
1825
            if (types.isEmpty()){
1826
                return 0l;
1827
            }else{
1828
                criteria.add(Restrictions.in("type", types) );
1829
            }
1830
        }
1831
        //count
1832
        criteria.setProjection(Projections.rowCount());
1833
        long result = ((Number)criteria.uniqueResult()).longValue();
1834

    
1835
        return result;
1836
    }
1837

    
1838
    @Override
1839
    public List<TaxonRelationship> getTaxonRelationships(Set<TaxonRelationshipType> types,
1840
            Integer pageSize, Integer pageNumber,
1841
            List<OrderHint> orderHints, List<String> propertyPaths) {
1842
        Criteria criteria = getSession().createCriteria(TaxonRelationship.class);
1843

    
1844
        if (types != null) {
1845
            if (types.isEmpty()){
1846
                return new ArrayList<>();
1847
            }else{
1848
                criteria.add(Restrictions.in("type", types) );
1849
            }
1850
        }
1851
        addOrder(criteria,orderHints);
1852

    
1853
        if(pageSize != null) {
1854
            criteria.setMaxResults(pageSize);
1855
            if(pageNumber != null) {
1856
                criteria.setFirstResult(pageNumber * pageSize);
1857
            } else {
1858
                criteria.setFirstResult(0);
1859
            }
1860
        }
1861

    
1862
        List<TaxonRelationship> results = criteria.list();
1863
        defaultBeanInitializer.initializeAll(results, propertyPaths);
1864

    
1865
        return results;
1866
    }
1867

    
1868
    @Override
1869
    public  List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCache(Integer limit, String pattern){
1870
        Session session = getSession();
1871
        Query query = null;
1872
        if (pattern != null){
1873
            query = session.createQuery("select taxonBase.uuid, taxonBase.id, taxonBase.titleCache, taxonBase.name.rank from TaxonBase as taxonBase where taxonBase.titleCache like :pattern");
1874
            pattern = pattern.replace("*", "%");
1875
            pattern = pattern.replace("?", "_");
1876
            pattern = pattern + "%";
1877
            query.setParameter("pattern", pattern);
1878
            } else {
1879
            query = session.createQuery("select taxonBase.uuid, taxonBase.id, taxonBase.titleCache, taxonBase.name.rank from TaxonBase as taxonBase"  );
1880
        }
1881
        if (limit != null){
1882
           query.setMaxResults(limit);
1883
        }
1884

    
1885
        return getUuidAndTitleCache(query);
1886
    }
1887

    
1888
    @Override
1889
    protected List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCache(Query query){
1890
        List<UuidAndTitleCache<TaxonBase>> list = new ArrayList<UuidAndTitleCache<TaxonBase>>();
1891

    
1892
        List<Object[]> result = query.list();
1893
        if (result != null && !result.isEmpty()){
1894
            if (result.iterator().next().length == 4){
1895
                Collections.sort(result, new UuidAndTitleCacheTaxonComparator());
1896
            }
1897
        }
1898

    
1899
        for(Object[] object : result){
1900
            list.add(new UuidAndTitleCache<TaxonBase>((UUID) object[0],(Integer) object[1], (String) object[2]));
1901
        }
1902
        return list;
1903
    }
1904

    
1905

    
1906
}
(3-3/5)