Project

General

Profile

Download (77.1 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
 * @created 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<NamedArea>();
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<UUID>();
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
                subMisappliedNames.setParameter("rType", TaxonRelationshipType.MISAPPLIED_NAME_FOR());
372
                if(doAreaRestriction){
373
                    subMisappliedNames.setParameterList("namedAreasUuids", namedAreasUuids);
374
                }
375
                if(classification != null){
376
                    subMisappliedNames.setParameter("classification", classification);
377
                }
378
            }
379

    
380
            if(doCommonNames){
381
                // find Taxa
382
                subCommonNames = getSession().createQuery(commonNameSubSelect).setParameter("queryString", hqlQueryString);
383

    
384
                if(doAreaRestriction){
385
                    subCommonNames.setParameterList("namedAreasUuids", namedAreasUuids);
386
                }
387
                if(classification != null){
388
                    subCommonNames.setParameter("classification", classification);
389
                }
390
            }
391

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

    
407

    
408
           // if (doTaxa && doSynonyms){
409
                if(synonyms.size()>0 && taxa.size()>0){
410
                    hql = "select " + selectWhat;
411
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
412
                    // also return the computed isOrphaned flag
413
                    if (doNotReturnFullEntities &&  !doCount ){
414
                        hql += ", case when t.id in (:taxa) then 'taxon' else 'synonym' end, " +
415
                                " 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 ";
416
                    }
417
                    hql +=  " from %s t " +
418
                            " where (t.id in (:taxa) OR t.id in (:synonyms)) ";
419
                }else if (synonyms.size()>0 ){
420
                    hql = "select " + selectWhat;
421
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
422
                    // also return the computed isOrphaned flag
423
                    if (doNotReturnFullEntities &&  !doCount ){
424
                        hql += ", 'synonym', 'false' ";
425

    
426
                    }
427
                    hql +=  " from %s t " +
428
                            " where t.id in (:synonyms) ";
429

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

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

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

    
458
            hql = String.format(hql, classString);
459

    
460

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

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

    
476
                hql += orderBy;
477
            }
478

    
479
            if(logger.isDebugEnabled()){ logger.debug("hql: " + hql);}
480
            Query query = getSession().createQuery(hql);
481

    
482

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

    
502
            return query;
503
    }
504

    
505

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

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

    
525
        if(pageSize != null &&  !doCount && query != null) {
526
            query.setMaxResults(pageSize);
527
            if(pageNumber != null) {
528
                query.setFirstResult(pageNumber * pageSize);
529
            }
530
        }
531

    
532
        return query;
533
    }
534

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

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

    
550
        Query query = getSession().createQuery(hql);
551

    
552
        query.setParameter("queryString", matchMode.queryStringFrom(queryString));
553

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

    
563
    @Override
564
    public long countTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, boolean doCommonNames,
565
            boolean doIncludeAuthors, String queryString, Classification classification,
566
        MatchMode matchMode, Set<NamedArea> namedAreas) {
567

    
568
        boolean doCount = true;
569
        /*
570
        boolean doTaxa = true;
571
        boolean doSynonyms = true;
572
        if (clazz.equals(Taxon.class)){
573
            doSynonyms = false;
574
        } else if (clazz.equals(Synonym.class)){
575
            doTaxa = false;
576
        }
577
        */
578
        String searchField = doIncludeAuthors ? "titleCache": "nameCache";
579

    
580
        Query query = prepareTaxaByName(doTaxa, doSynonyms, doMisappliedNames, doCommonNames, searchField, queryString, classification, matchMode, namedAreas, null, null, null, doCount);
581
        if (query != null) {
582
            return (Long)query.uniqueResult();
583
        }else{
584
            return 0;
585
        }
586
    }
587

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

    
606

    
607
    @Override
608
    public List<TaxonBase> getAllTaxonBases(Integer pagesize, Integer page) {
609
        return super.list(pagesize, page);
610
    }
611

    
612
    @Override
613
    public List<Synonym> getAllSynonyms(Integer limit, Integer start) {
614
        Criteria criteria = getSession().createCriteria(Synonym.class);
615

    
616
        if(limit != null) {
617
            criteria.setFirstResult(start);
618
            criteria.setMaxResults(limit);
619
        }
620

    
621
        return criteria.list();
622
    }
623

    
624
    @Override
625
    public List<Taxon> getAllTaxa(Integer limit, Integer start) {
626
        Criteria criteria = getSession().createCriteria(Taxon.class);
627

    
628
        if(limit != null) {
629
            criteria.setFirstResult(start);
630
            criteria.setMaxResults(limit);
631
        }
632

    
633
        return criteria.list();
634
    }
635

    
636

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

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

    
651
        taxonBase.removeSources();
652

    
653

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

    
662
       return super.delete(taxonBase);
663

    
664
    }
665

    
666
    @Override
667
    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) {
668

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

    
678
    }
679

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

    
683
        Criteria crit = getSession().createCriteria(type);
684

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

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

    
709
        return null;
710
    }
711

    
712
    @Override
713
    public List<? extends TaxonBase> findByUuids(List<UUID> uuids, List<Criterion> criteria, List<String> propertyPaths) {
714

    
715
        Criteria crit = getSession().createCriteria(type);
716

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

    
730
        List<? extends TaxonBase> results = crit.list();
731

    
732
        defaultBeanInitializer.initializeAll(results, propertyPaths);
733
        return results;
734
    }
735

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

    
746

    
747
    @Override
748
    public int countMatchesByName(String queryString, MatchMode matchMode, boolean onlyAcccepted, List<Criterion> criteria) {
749
        checkNotInPriorView("TaxonDaoHibernateImpl.countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted, List<Criterion> criteria)");
750
        Criteria crit = getSession().createCriteria(type);
751
        crit.add(Restrictions.ilike("titleCache", matchMode.queryStringFrom(queryString)));
752
        if(criteria != null){
753
            for (Criterion criterion : criteria) {
754
                crit.add(criterion);
755
            }
756
        }
757
        crit.setProjection(Projections.rowCount());
758
        int result = ((Number)crit.list().get(0)).intValue();
759
        return result;
760
    }
761

    
762
    @Override
763
    public int countTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Direction direction) {
764
        AuditEvent auditEvent = getAuditEventFromContext();
765
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
766
            Query query = null;
767

    
768
            if(type == null) {
769
                query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon");
770
            } else {
771
                query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon and taxonRelationship.type = :type");
772
                query.setParameter("type",type);
773
            }
774
            query.setParameter("relatedTaxon", taxon);
775

    
776
            return ((Long)query.uniqueResult()).intValue();
777
        } else {
778
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
779
            query.add(AuditEntity.relatedId(direction.toString()).eq(taxon.getId()));
780
            query.addProjection(AuditEntity.id().count());
781

    
782
            if(type != null) {
783
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
784
            }
785

    
786
            return ((Long)query.getSingleResult()).intValue();
787
        }
788
    }
789

    
790

    
791
    @Override
792
    public int countSynonyms(boolean onlyAttachedToTaxon) {
793
        AuditEvent auditEvent = getAuditEventFromContext();
794
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
795
            Query query = null;
796

    
797
            String queryStr = "SELECT count(syn) FROM Synonym syn";
798
            if (onlyAttachedToTaxon){
799
                queryStr += " WHERE syn.acceptedTaxon IS NOT NULL";
800
            }
801
            query = getSession().createQuery(queryStr);
802

    
803
            return ((Long)query.uniqueResult()).intValue();
804
        } else {
805
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(Synonym.class,auditEvent.getRevisionNumber());
806
            if (onlyAttachedToTaxon){
807
                query.add(new NotNullAuditExpression(new EntityPropertyName("acceptedTaxon")));
808
            }
809
            query.addProjection(AuditEntity.id().count());
810

    
811
            return ((Long)query.getSingleResult()).intValue();
812
        }
813
    }
814

    
815
    @Override
816
    public long countSynonyms(Taxon taxon, SynonymType type) {
817
        AuditEvent auditEvent = getAuditEventFromContext();
818
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
819
            Criteria criteria = getSession().createCriteria(Synonym.class);
820

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

    
832
            if(type != null) {
833
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
834
            }
835

    
836
            return (Long)query.getSingleResult();
837
        }
838
    }
839

    
840
    @Override
841
    public int countSynonyms(Synonym synonym, SynonymType type) {
842
        AuditEvent auditEvent = getAuditEventFromContext();
843
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
844
            Criteria criteria = getSession().createCriteria(Synonym.class);
845

    
846
            criteria.add(Restrictions.isNotNull("acceptedTaxon"));
847
            if(type != null) {
848
                criteria.add(Restrictions.eq("type", type));
849
            }
850

    
851
            criteria.setProjection(Projections.rowCount());
852
            return ((Number)criteria.uniqueResult()).intValue();
853
        } else {
854
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(Synonym.class,auditEvent.getRevisionNumber());
855
            query.add(new NotNullAuditExpression(new EntityPropertyName("acceptedTaxon")));
856
            query.addProjection(AuditEntity.id().count());
857

    
858
            if(type != null) {
859
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
860
            }
861

    
862
            return ((Long)query.getSingleResult()).intValue();
863
        }
864
    }
865

    
866
    @Override
867
    public int countTaxaByName(Class<? extends TaxonBase> clazz, String genusOrUninomial, String infraGenericEpithet, String specificEpithet,	String infraSpecificEpithet, Rank rank) {
868
        checkNotInPriorView("TaxonDaoHibernateImpl.countTaxaByName(Boolean accepted, String genusOrUninomial,	String infraGenericEpithet, String specificEpithet,	String infraSpecificEpithet, Rank rank)");
869
        Criteria criteria = null;
870

    
871
        criteria = getSession().createCriteria(clazz);
872

    
873
        criteria.setFetchMode( "name", FetchMode.JOIN );
874
        criteria.createAlias("name", "name");
875

    
876
        if(genusOrUninomial == null) {
877
            criteria.add(Restrictions.isNull("name.genusOrUninomial"));
878
        } else if(!genusOrUninomial.equals("*")) {
879
            criteria.add(Restrictions.eq("name.genusOrUninomial", genusOrUninomial));
880
        }
881

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

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

    
893
        }
894

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

    
901
        if(rank != null) {
902
            criteria.add(Restrictions.eq("name.rank", rank));
903
        }
904

    
905
        criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
906

    
907
        return ((Number)criteria.uniqueResult()).intValue();
908
    }
909

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

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

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

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

    
939
        }
940

    
941
        if(infraSpecificEpithet == null) {
942
            criteria.add(Restrictions.isNull("name.infraSpecificEpithet"));
943
        } else if(!infraSpecificEpithet.equals("*")) {
944
            criteria.add(Restrictions.eq("name.infraSpecificEpithet", infraSpecificEpithet));
945
        }
946

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

    
953
        if(rank != null) {
954
            criteria.add(Restrictions.eq("name.rank", rank));
955
        }
956

    
957
        if(pageSize != null) {
958
            criteria.setMaxResults(pageSize);
959
            if(pageNumber != null) {
960
                criteria.setFirstResult(pageNumber * pageSize);
961
            } else {
962
                criteria.setFirstResult(0);
963
            }
964
        }
965

    
966
        return criteria.list();
967
    }
968

    
969
    @Override
970
    public List<TaxonRelationship> getTaxonRelationships(Taxon taxon, TaxonRelationshipType type,
971
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints,
972
            List<String> propertyPaths, Direction direction) {
973

    
974
        AuditEvent auditEvent = getAuditEventFromContext();
975
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
976

    
977
            Criteria criteria = getSession().createCriteria(TaxonRelationship.class);
978

    
979
            if(direction != null) {
980
                criteria.add(Restrictions.eq(direction.name(), taxon));
981
            } else {
982
                criteria.add(Restrictions.or(
983
                        Restrictions.eq(Direction.relatedFrom.name(), taxon),
984
                        Restrictions.eq(Direction.relatedTo.name(), taxon))
985
                    );
986
            }
987

    
988
            if(type != null) {
989
                criteria.add(Restrictions.eq("type", type));
990
            }
991

    
992
            addOrder(criteria,orderHints);
993

    
994
            if(pageSize != null) {
995
                criteria.setMaxResults(pageSize);
996
                if(pageNumber != null) {
997
                    criteria.setFirstResult(pageNumber * pageSize);
998
                } else {
999
                    criteria.setFirstResult(0);
1000
                }
1001
            }
1002

    
1003
            @SuppressWarnings("unchecked")
1004
            List<TaxonRelationship> result = criteria.list();
1005
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1006

    
1007
            return result;
1008
        } else {
1009
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1010
            query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));
1011

    
1012
            if(type != null) {
1013
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1014
            }
1015

    
1016
            if(pageSize != null) {
1017
                query.setMaxResults(pageSize);
1018
                if(pageNumber != null) {
1019
                    query.setFirstResult(pageNumber * pageSize);
1020
                } else {
1021
                    query.setFirstResult(0);
1022
                }
1023
            }
1024

    
1025
            List<TaxonRelationship> result = query.getResultList();
1026
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1027

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

    
1037
            return result;
1038
        }
1039
    }
1040

    
1041
    class TaxonRelationshipFromTaxonComparator implements Comparator<TaxonRelationship> {
1042

    
1043
        @Override
1044
        public int compare(TaxonRelationship o1, TaxonRelationship o2) {
1045
            if (o1.equals(o2)){
1046
                return 0;
1047
            }
1048
            int result = o1.getFromTaxon().getTitleCache().compareTo(o2.getFromTaxon().getTitleCache());
1049
            if (result == 0 ){
1050
                result = o1.getUuid().compareTo(o2.getUuid());
1051
            }
1052
            return result;
1053
        }
1054

    
1055
    }
1056

    
1057
    @Override
1058
    public List<Synonym> getSynonyms(Taxon taxon, SynonymType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
1059
        AuditEvent auditEvent = getAuditEventFromContext();
1060
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1061
            Criteria criteria = getSession().createCriteria(Synonym.class);
1062

    
1063
            criteria.add(Restrictions.eq("acceptedTaxon", taxon));
1064
            if(type != null) {
1065
                criteria.add(Restrictions.eq("type", type));
1066
            }
1067

    
1068
            addOrder(criteria,orderHints);
1069

    
1070
            if(pageSize != null) {
1071
                criteria.setMaxResults(pageSize);
1072
                if(pageNumber != null) {
1073
                    criteria.setFirstResult(pageNumber * pageSize);
1074
                } else {
1075
                    criteria.setFirstResult(0);
1076
                }
1077
            }
1078

    
1079
            @SuppressWarnings("unchecked")
1080
            List<Synonym> result = criteria.list();
1081
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1082

    
1083
            return result;
1084
        } else {
1085
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(Synonym.class,auditEvent.getRevisionNumber());
1086
            query.add(AuditEntity.relatedId("acceptedTaxon").eq(taxon.getId()));
1087

    
1088
            if(type != null) {
1089
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1090
            }
1091

    
1092
            if(pageSize != null) {
1093
                query.setMaxResults(pageSize);
1094
                if(pageNumber != null) {
1095
                    query.setFirstResult(pageNumber * pageSize);
1096
                } else {
1097
                    query.setFirstResult(0);
1098
                }
1099
            }
1100

    
1101
            List<Synonym> result = query.getResultList();
1102
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1103

    
1104
            return result;
1105
        }
1106
    }
1107

    
1108
    @Override
1109
    public void rebuildIndex() {
1110
        FullTextSession fullTextSession = Search.getFullTextSession(getSession());
1111

    
1112
        for(TaxonBase taxonBase : list(null,null)) { // re-index all taxon base
1113
            Hibernate.initialize(taxonBase.getName());
1114
            fullTextSession.index(taxonBase);
1115
        }
1116
        fullTextSession.flushToIndexes();
1117
    }
1118

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

    
1141
    @Override
1142
    public Taxon acceptedTaxonFor(Synonym synonym, Classification classificationFilter, List<String> propertyPaths){
1143

    
1144
        String hql = prepareListAcceptedTaxaFor(classificationFilter, false);
1145

    
1146
        Query query = getSession().createQuery(hql);
1147

    
1148
        query.setParameter("synonym", synonym);
1149

    
1150
        if(classificationFilter != null){
1151
            query.setParameter("classificationFilter", classificationFilter);
1152
        }
1153

    
1154
        @SuppressWarnings("unchecked")
1155
        List<Taxon> result = query.list();
1156

    
1157
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1158

    
1159
        return result.isEmpty()? null: result.get(0);
1160
    }
1161

    
1162
    @Override
1163
    public long countAcceptedTaxonFor(Synonym synonym, Classification classificationFilter){
1164

    
1165
        String hql = prepareListAcceptedTaxaFor(classificationFilter, true);
1166

    
1167
        Query query = getSession().createQuery(hql);
1168

    
1169
        query.setParameter("synonym", synonym);
1170

    
1171
        if(classificationFilter != null){
1172
            query.setParameter("classificationFilter", classificationFilter);
1173
        }
1174

    
1175
        Long count = Long.parseLong(query.uniqueResult().toString());
1176

    
1177
        return count;
1178

    
1179
    }
1180

    
1181

    
1182
    /**
1183
     * @param classificationFilter
1184
     * @param orderHints
1185
     * @return
1186
     */
1187
    private String prepareListAcceptedTaxaFor(Classification classificationFilter, boolean doCount) {
1188

    
1189
        String hql;
1190
        String hqlSelect = "SELECT " + (doCount? "COUNT(taxon)" : "taxon") + " FROM Synonym as syn JOIN syn.acceptedTaxon as taxon ";
1191
        String hqlWhere = " WHERE syn = :synonym";
1192

    
1193
        if(classificationFilter != null){
1194
            hqlSelect += " JOIN taxon.taxonNodes AS taxonNode";
1195
            hqlWhere += " AND taxonNode.classification = :classificationFilter";
1196
        }
1197
        hql = hqlSelect + hqlWhere;
1198
        return hql;
1199
    }
1200

    
1201
    @Override
1202
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, Integer limit, String pattern) {
1203
        int classificationId = classification.getId();
1204
        // StringBuffer excludeUuids = new StringBuffer();
1205

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

    
1220

    
1221

    
1222
         Query query = getSession().createQuery(queryString);
1223

    
1224

    
1225
         if (limit != null){
1226
             query.setMaxResults(limit);
1227
         }
1228

    
1229
         if (pattern != null && !pattern.equals("?")){
1230
             query.setParameter("pattern", pattern);
1231
         }
1232
         @SuppressWarnings("unchecked")
1233
         List<Object[]> result = query.list();
1234

    
1235
         if(result.size() == 0){
1236
             return null;
1237
         }else{
1238
             List<UuidAndTitleCache<TaxonNode>> list = new ArrayList<UuidAndTitleCache<TaxonNode>>(result.size());
1239

    
1240
             for (Object object : result){
1241

    
1242
                 Object[] objectArray = (Object[]) object;
1243

    
1244
                 UUID uuid = (UUID)objectArray[0];
1245
                 Integer id = (Integer) objectArray[1];
1246
                 String titleCache = (String) objectArray[2];
1247

    
1248
                 list.add(new UuidAndTitleCache<TaxonNode>(TaxonNode.class, uuid, id, titleCache));
1249
             }
1250

    
1251
             return list;
1252
         }
1253
    }
1254

    
1255

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

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

    
1280

    
1281

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

    
1295
        }
1296

    
1297
        return taxonNames;
1298
    }
1299

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

    
1306
        return zooNames;
1307

    
1308
    }
1309

    
1310
    @Override
1311
    public List<TaxonName> findIdenticalTaxonNames(List<String> propertyPaths) {
1312

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

    
1315
        @SuppressWarnings("unchecked")
1316
        List<TaxonName> zooNames = query.list();
1317

    
1318
        TaxonNameComparator taxComp = new TaxonNameComparator();
1319
        Collections.sort(zooNames, taxComp);
1320

    
1321
        for (TaxonName taxonName: zooNames){
1322
            defaultBeanInitializer.initialize(taxonName, propertyPaths);
1323
        }
1324

    
1325
        return zooNames;
1326
    }
1327

    
1328
    @Override
1329
    public List<TaxonName> findIdenticalNamesNew(List<String> propertyPaths){
1330

    
1331
        //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)
1332
        Query query = getSession().createQuery("Select id from Reference where titleCache like 'Fauna Europaea database'");
1333
        List<String> secRefFauna = query.list();
1334
        query = getSession().createQuery("Select id from Reference where titleCache like 'ERMS'");
1335
        List<String> secRefErms = query.list();
1336
        //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");
1337
        //Get all names of fauna europaea
1338
        query = getSession().createQuery("select zn.nameCache from ZoologicalName zn, TaxonBase tb where tb.name = zn and tb.sec.id = :secRefFauna");
1339
        query.setParameter("secRefFauna", secRefFauna.get(0));
1340
        List<String> namesFauna= query.list();
1341

    
1342
        //Get all names of erms
1343

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

    
1347
        List<String> namesErms = query.list();
1348
        /*TaxonNameComparator comp = new TaxonNameComparator();
1349
        Collections.sort(namesFauna);
1350
        Collections.sort(namesErms);
1351
        */
1352
        List <String> identicalNames = new ArrayList<String>();
1353
        String predecessor = "";
1354

    
1355
        for (String nameFauna: namesFauna){
1356
            if (namesErms.contains(nameFauna)){
1357
                identicalNames.add(nameFauna);
1358
            }
1359
        }
1360

    
1361

    
1362
        query = getSession().createQuery("from ZoologicalName zn where zn.nameCache IN (:identicalNames)");
1363
        query.setParameterList("identicalNames", identicalNames);
1364
        List<TaxonName> result = query.list();
1365
        TaxonName tempName = result.get(0);
1366

    
1367
        Iterator<IdentifiableSource> sources = tempName.getSources().iterator();
1368

    
1369
        TaxonNameComparator taxComp = new TaxonNameComparator();
1370
        Collections.sort(result, taxComp);
1371
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1372
        return result;
1373

    
1374
    }
1375

    
1376
//
1377
//
1378
//    @Override
1379
//    public String getPhylumName(TaxonName name){
1380
//        List results = new ArrayList();
1381
//        try{
1382
//        Query query = getSession().createSQLQuery("select getPhylum("+ name.getId()+");");
1383
//        results = query.list();
1384
//        }catch(Exception e){
1385
//            System.err.println(name.getUuid());
1386
//            return null;
1387
//        }
1388
//        System.err.println("phylum of "+ name.getTitleCache() );
1389
//        return (String)results.get(0);
1390
//    }
1391

    
1392

    
1393
    @Override
1394
    public long countTaxaByCommonName(String searchString,
1395
            Classification classification, MatchMode matchMode,
1396
            Set<NamedArea> namedAreas) {
1397
        boolean doCount = true;
1398
        Query query = prepareTaxaByCommonName(searchString, classification, matchMode, namedAreas, null, null, doCount, false);
1399
        if (query != null && !query.list().isEmpty()) {
1400
            Object o = query.uniqueResult();
1401
            if(o != null) {
1402
                return (Long)o;
1403
            }
1404
        }
1405
        return 0;
1406
    }
1407

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

    
1410
           boolean doAreaRestriction = areasExpanded.size() > 0;
1411
           String 	doAreaRestrictionSubSelect = "select %s.id from" +
1412
                " Distribution e" +
1413
                " join e.inDescription d" +
1414
                " join d.taxon t" +
1415
                (classification != null ? " join t.taxonNodes as tn " : " ");
1416

    
1417
           String 	doAreaRestrictionMisappliedNameSubSelect = "select %s.id from" +
1418
            " Distribution e" +
1419
            " join e.inDescription d" +
1420
            " join d.taxon t";
1421

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

    
1425
           String doTaxonNameJoin =   " join t.name n ";
1426

    
1427
           String doSynonymNameJoin =  	" join t.synonyms s join s.name sn";
1428

    
1429
           String doMisappliedNamesJoin = " left join t.relationsFromThisTaxon as rft" +
1430
                " left join rft.relatedTo as rt" +
1431
                (classification != null ? " left join rt.taxonNodes as tn2" : " ") +
1432
                " left join rt.name as n2" +
1433
                " left join rft.type as rtype";
1434

    
1435
           String doCommonNamesJoin =   "join t.descriptions as description "+
1436
                   "left join description.descriptionElements as com " +
1437
                   "left join com.feature f ";
1438

    
1439

    
1440
           String doClassificationWhere = " tn.classification = :classification";
1441
           String doClassificationForMisappliedNamesWhere = " tn2 .classification = :classification";
1442

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

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

    
1448
           String doRelationshipTypeComparison = " rtype = :rType ";
1449

    
1450
        String taxonSubselect = null;
1451
        String synonymSubselect = null;
1452
        String misappliedSelect = null;
1453
        String commonNameSubselect = null;
1454

    
1455
        if(classification != null ){
1456
            if (!doIncludeMisappliedNames){
1457
                if(doAreaRestriction){
1458
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
1459
                    " WHERE " + doAreaRestrictionWhere +
1460
                    " AND " + doClassificationWhere +
1461
                    " AND " + String.format(doSearchFieldWhere, "n");
1462
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
1463
                    " WHERE " + doAreaRestrictionWhere +
1464
                    " AND " + doClassificationWhere +
1465
                    " AND " + String.format(doSearchFieldWhere, "sn");
1466
                    commonNameSubselect =  String.format(doAreaRestrictionSubSelect, "t") + doCommonNamesJoin +
1467
                            " WHERE " +  doAreaRestrictionWhere + " AND " + doClassificationWhere +
1468
                            " AND " + String.format(doSearchFieldWhere, "n")
1469
                            + " AND " + doCommonNamesRestrictionWhere;
1470
                } else {
1471
                    taxonSubselect = String.format(doTaxonSubSelect, "t" )+ doTaxonNameJoin +
1472
                    " WHERE " + doClassificationWhere +
1473
                    " AND " + String.format(doSearchFieldWhere, "n");
1474
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
1475
                    " WHERE " + doClassificationWhere +
1476
                    " AND " + String.format(doSearchFieldWhere, "sn");
1477
                    commonNameSubselect =String.format(doTaxonSubSelect, "t" )+ doCommonNamesJoin +
1478
                            " WHERE " + doClassificationWhere +
1479
                            " AND " + doCommonNamesRestrictionWhere;
1480
                }
1481
            }else{ //misappliedNames included
1482
                if(doAreaRestriction){
1483
                    misappliedSelect = String.format(doAreaRestrictionMisappliedNameSubSelect, "t") + doTaxonNameJoin + doMisappliedNamesJoin  +
1484
                    " WHERE " + doAreaRestrictionWhere +
1485
                    " AND " + String.format(doSearchFieldWhere, "n") +
1486
                    " AND " + doClassificationForMisappliedNamesWhere +
1487
                    " AND " + doRelationshipTypeComparison;
1488

    
1489
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
1490
                    " WHERE " + doAreaRestrictionWhere +
1491
                    " AND "+ String.format(doSearchFieldWhere, "n") + " AND "+ doClassificationWhere;
1492

    
1493
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
1494
                    " WHERE " + doAreaRestrictionWhere +
1495
                    " AND " + doClassificationWhere + " AND " +  String.format(doSearchFieldWhere, "sn");
1496

    
1497
                    commonNameSubselect= String.format(doAreaRestrictionSubSelect, "t")+ doCommonNamesJoin +
1498
                            " WHERE " + doAreaRestrictionWhere +
1499
                            " AND "+ doClassificationWhere + " AND " + doCommonNamesRestrictionWhere;
1500
                } else {
1501
                    misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin +
1502
                    " WHERE " + String.format(doSearchFieldWhere, "n") +
1503
                    " AND " + doClassificationForMisappliedNamesWhere +
1504
                    " AND " + doRelationshipTypeComparison;
1505

    
1506
                    taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin +
1507
                    " WHERE " +  String.format(doSearchFieldWhere, "n") +
1508
                    " AND "+ doClassificationWhere;
1509

    
1510
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
1511
                    " WHERE " + doClassificationWhere +
1512
                    " AND " +  String.format(doSearchFieldWhere, "sn");
1513

    
1514
                    commonNameSubselect= String.format(doTaxonSubSelect, "t")+ doCommonNamesJoin +
1515
                            " WHERE " + doClassificationWhere + " AND " + doCommonNamesRestrictionWhere;
1516

    
1517
                }
1518
            }
1519
        } else {
1520
            if(doAreaRestriction){
1521
                misappliedSelect = String.format(doAreaRestrictionMisappliedNameSubSelect, "t") + doTaxonNameJoin + doMisappliedNamesJoin +
1522
                " WHERE " + doAreaRestrictionWhere +
1523
                " AND " + String.format(doSearchFieldWhere, "n")+
1524
                " AND " + doRelationshipTypeComparison;
1525

    
1526
                taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
1527
                " WHERE " + doAreaRestrictionWhere +
1528
                " AND " + String.format(doSearchFieldWhere, "n");
1529

    
1530
                synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
1531
                " WHERE " +   doAreaRestrictionWhere +
1532
                " AND " +  String.format(doSearchFieldWhere, "sn");
1533
                commonNameSubselect = String.format(doAreaRestrictionSubSelect, "t")+ doCommonNamesJoin +
1534
                        " WHERE " + doAreaRestrictionWhere +
1535
                        " AND " + doCommonNamesRestrictionWhere;
1536

    
1537

    
1538
            } else {
1539
                misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin + " WHERE " +  String.format(doSearchFieldWhere, "n") + " AND " + doRelationshipTypeComparison;
1540
                taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "n");
1541
                synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "sn");
1542
                commonNameSubselect = String.format(doTaxonSubSelect, "t" ) +doCommonNamesJoin + " WHERE "+  doCommonNamesRestrictionWhere;
1543

    
1544
            }
1545
        }
1546
        String[] result = {misappliedSelect, taxonSubselect, synonymSubselect, commonNameSubselect};
1547

    
1548
        return result;
1549
    }
1550

    
1551
	@Override
1552
	public List<UuidAndTitleCache<IdentifiableEntity>> getTaxaByCommonNameForEditor(
1553
			String titleSearchStringSqlized, Classification classification,
1554
			MatchMode matchMode, Set namedAreas) {
1555
	    List<Object> resultArray = new ArrayList<Object>();
1556
		Query query = prepareTaxaByCommonName(titleSearchStringSqlized, classification, matchMode, namedAreas, null, null, false, true);
1557
        if (query != null){
1558
            resultArray = query.list();
1559
            List<UuidAndTitleCache<IdentifiableEntity>> returnResult = new ArrayList<UuidAndTitleCache<IdentifiableEntity>>() ;
1560
            Object[] result;
1561
            for(int i = 0; i<resultArray.size();i++){
1562
            	result = (Object[]) resultArray.get(i);
1563
            	returnResult.add(new UuidAndTitleCache(Taxon.class, (UUID) result[0],(Integer)result[1], (String)result[2], new Boolean(result[4].toString()), null));
1564
            }
1565
            return returnResult;
1566
        }
1567

    
1568
		return null;
1569
	}
1570

    
1571

    
1572
	/**
1573
	 * @param
1574
	 * @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)
1575
	 */
1576
	@Override
1577
	public <S extends TaxonBase> int countByIdentifier(Class<S> clazz,
1578
			String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter, MatchMode matchmode) {
1579
		if (subtreeFilter == null){
1580
			return countByIdentifier(clazz, identifier, identifierType, matchmode);
1581
		}
1582

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

    
1586
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
1587
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
1588

    
1589
		getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
1590
		String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
1591
		String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
1592
		String synTreeJoin = isSynonym ? " LEFT JOIN c.acceptedTaxon as acc LEFT JOIN acc.taxonNodes synTn  " : "";
1593
		String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
1594
		String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
1595

    
1596
		String queryString = "SELECT count(*)  FROM %s as c " +
1597
                " INNER JOIN c.identifiers as ids " +
1598
                accTreeJoin +
1599
                synTreeJoin +
1600
                " WHERE (1=1) " +
1601
                	"  AND ( " + accWhere + " OR " + synWhere + ")";
1602
		queryString = String.format(queryString, clazzParam.getSimpleName());
1603

    
1604
		if (identifier != null){
1605
			if (matchmode == null || matchmode == MatchMode.EXACT){
1606
				queryString += " AND ids.identifier = '"  + identifier + "'";
1607
			}else {
1608
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
1609
			}
1610
		}
1611
		if (identifierType != null){
1612
        	queryString += " AND ids.type = :type";
1613
        }
1614

    
1615
		Query query = getSession().createQuery(queryString);
1616
        if (identifierType != null){
1617
        	query.setEntity("type", identifierType);
1618
        }
1619

    
1620
		Long c = (Long)query.uniqueResult();
1621
        return c.intValue();
1622
	}
1623

    
1624
	@Override
1625
	public <S extends TaxonBase> List<Object[]> findByIdentifier(
1626
			Class<S> clazz, String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter,
1627
			MatchMode matchmode, boolean includeEntity,
1628
			Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
1629

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

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

    
1642
		String queryString = "SELECT ids.type, ids.identifier, %s " +
1643
				" FROM %s as c " +
1644
                " INNER JOIN c.identifiers as ids " +
1645
                accTreeJoin +
1646
				synTreeJoin +
1647
                " WHERE (1=1) " +
1648
                	" AND ( " + accWhere + " OR " + synWhere + ")";
1649
		queryString = String.format(queryString, (includeEntity ? "c":"c.uuid, c.titleCache") , clazzParam.getSimpleName());
1650

    
1651
		//Matchmode and identifier
1652
		if (identifier != null){
1653
			if (matchmode == null || matchmode == MatchMode.EXACT){
1654
				queryString += " AND ids.identifier = '"  + identifier + "'";
1655
			}else {
1656
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
1657
			}
1658
		}
1659
        if (identifierType != null){
1660
        	queryString += " AND ids.type = :type";
1661
        }
1662
        //order
1663
        queryString +=" ORDER BY ids.type.uuid, ids.identifier, c.uuid ";
1664

    
1665
		Query query = getSession().createQuery(queryString);
1666

    
1667
		//parameters
1668
		if (identifierType != null){
1669
        	query.setEntity("type", identifierType);
1670
        }
1671

    
1672
        //paging
1673
        setPagingParameter(query, pageSize, pageNumber);
1674

    
1675
        List<Object[]> results = query.list();
1676
        //initialize
1677
        if (includeEntity){
1678
        	List<S> entities = new ArrayList<S>();
1679
        	for (Object[] result : results){
1680
        		entities.add((S)result[2]);
1681
        	}
1682
        	defaultBeanInitializer.initializeAll(entities, propertyPaths);
1683
        }
1684
        return results;
1685
	}
1686

    
1687
    /**
1688
     * {@inheritDoc}
1689
     * @see #countByIdentifier(Class, String, DefinedTerm, TaxonNode, MatchMode)
1690
     */
1691
    @Override
1692
    public <S extends TaxonBase> long countByMarker(Class<S> clazz, MarkerType markerType,
1693
            Boolean markerValue, TaxonNode subtreeFilter) {
1694
        if (markerType == null){
1695
            return 0;
1696
        }
1697

    
1698
        if (subtreeFilter == null){
1699
            return countByMarker(clazz, markerType, markerValue);
1700
        }
1701

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

    
1705
        boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
1706
        boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
1707

    
1708
        getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
1709
        String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
1710
        String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
1711
        String synTreeJoin = isSynonym ? " LEFT JOIN c.acceptedTaxon acc LEFT JOIN acc.taxonNodes synTn  " : "";
1712
        String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
1713
        String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
1714

    
1715
        String queryString = "SELECT count(*)  FROM %s as c " +
1716
                " INNER JOIN c.markers as mks " +
1717
                accTreeJoin +
1718
                synTreeJoin +
1719
                " WHERE (1=1) " +
1720
                    "  AND ( " + accWhere + " OR " + synWhere + ")";
1721
        queryString = String.format(queryString, clazzParam.getSimpleName());
1722

    
1723
        if (markerValue != null){
1724
            queryString += " AND mks.flag = :flag";
1725
        }
1726
        if (markerType != null){
1727
            queryString += " AND mks.markerType = :type";
1728
        }
1729

    
1730
        Query query = getSession().createQuery(queryString);
1731
        if (markerType != null){
1732
            query.setEntity("type", markerType);
1733
        }
1734
        if (markerValue != null){
1735
            query.setBoolean("flag", markerValue);
1736
        }
1737

    
1738
        Long c = (Long)query.uniqueResult();
1739
        return c;
1740
    }
1741

    
1742
    /**
1743
     * {@inheritDoc}
1744
     */
1745
    @Override
1746
    public <S extends TaxonBase> List<Object[]> findByMarker(Class<S> clazz, MarkerType markerType,
1747
            Boolean markerValue, TaxonNode subtreeFilter, boolean includeEntity,
1748
            TaxonTitleType titleType, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
1749
        checkNotInPriorView("TaxonDaoHibernateImpl.findByMarker(T clazz, String identifier, DefinedTerm identifierType, MatchMode matchmode, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)");
1750
        if (markerType == null){
1751
            return new ArrayList<Object[]>();
1752
        }
1753
        if (titleType == null){
1754
            titleType = TaxonTitleType.DEFAULT();
1755
        }
1756

    
1757
        Class<?> clazzParam = clazz == null ? type : clazz;
1758

    
1759
        boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
1760
        boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
1761
        getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
1762
        String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
1763
        String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
1764
        String synTreeJoin = isSynonym ? " LEFT JOIN c.acceptedTaxon as acc LEFT JOIN acc.taxonNodes synTn  " : "";
1765
        String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
1766
        String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
1767
        String selectParams = includeEntity ? "c" : titleType.hqlReplaceSelect("c.uuid, c.titleCache", "c.titleCache");
1768
        String titleTypeJoin = includeEntity ? "" : titleType.hqlJoin();
1769

    
1770
        String queryString = "SELECT mks.markerType, mks.flag, %s " +
1771
                " FROM %s as c " +
1772
                " INNER JOIN c.markers as mks " +
1773
                titleTypeJoin +
1774
                accTreeJoin +
1775
                synTreeJoin +
1776
                " WHERE (1=1) " +
1777
                    " AND ( " + accWhere + " OR " + synWhere + ")";
1778
        queryString = String.format(queryString, selectParams, clazzParam.getSimpleName());
1779

    
1780
        //type and value
1781
        if (markerValue != null){
1782
            queryString += " AND mks.flag = :flag";
1783
        }
1784
        queryString += " AND mks.markerType = :type";
1785
        //order
1786
        queryString +=" ORDER BY mks.markerType.uuid, mks.flag, c.uuid ";
1787

    
1788
        Query query = getSession().createQuery(queryString);
1789

    
1790
        //parameters
1791
        query.setEntity("type", markerType);
1792
        if (markerValue != null){
1793
            query.setBoolean("flag", markerValue);
1794
        }
1795

    
1796
        //paging
1797
        setPagingParameter(query, pageSize, pageNumber);
1798

    
1799
        List<Object[]> results = query.list();
1800
        //initialize
1801
        if (includeEntity){
1802
            List<S> entities = new ArrayList<S>();
1803
            for (Object[] result : results){
1804
                entities.add((S)result[2]);
1805
            }
1806
            defaultBeanInitializer.initializeAll(entities, propertyPaths);
1807
        }
1808
        return results;
1809
    }
1810

    
1811
    @Override
1812
    public long countTaxonRelationships(Set<TaxonRelationshipType> types) {
1813
        Criteria criteria = getSession().createCriteria(TaxonRelationship.class);
1814

    
1815
        if (types != null) {
1816
            if (types.isEmpty()){
1817
                return 0l;
1818
            }else{
1819
                criteria.add(Restrictions.in("type", types) );
1820
            }
1821
        }
1822
        //count
1823
        criteria.setProjection(Projections.rowCount());
1824
        long result = ((Number)criteria.uniqueResult()).longValue();
1825

    
1826
        return result;
1827
    }
1828

    
1829
    @Override
1830
    public List<TaxonRelationship> getTaxonRelationships(Set<TaxonRelationshipType> types,
1831
            Integer pageSize, Integer pageNumber,
1832
            List<OrderHint> orderHints, List<String> propertyPaths) {
1833
        Criteria criteria = getSession().createCriteria(TaxonRelationship.class);
1834

    
1835
        if (types != null) {
1836
            if (types.isEmpty()){
1837
                return new ArrayList<>();
1838
            }else{
1839
                criteria.add(Restrictions.in("type", types) );
1840
            }
1841
        }
1842
        addOrder(criteria,orderHints);
1843

    
1844
        if(pageSize != null) {
1845
            criteria.setMaxResults(pageSize);
1846
            if(pageNumber != null) {
1847
                criteria.setFirstResult(pageNumber * pageSize);
1848
            } else {
1849
                criteria.setFirstResult(0);
1850
            }
1851
        }
1852

    
1853
        List<TaxonRelationship> results = criteria.list();
1854
        defaultBeanInitializer.initializeAll(results, propertyPaths);
1855

    
1856
        return results;
1857
    }
1858

    
1859
    @Override
1860
    public  List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCache(Integer limit, String pattern){
1861
        Session session = getSession();
1862
        Query query = null;
1863
        if (pattern != null){
1864
            query = session.createQuery("select taxonBase.uuid, taxonBase.id, taxonBase.titleCache, taxonBase.name.rank from TaxonBase as taxonBase where taxonBase.titleCache like :pattern");
1865
            pattern = pattern.replace("*", "%");
1866
            pattern = pattern.replace("?", "_");
1867
            pattern = pattern + "%";
1868
            query.setParameter("pattern", pattern);
1869
            } else {
1870
            query = session.createQuery("select uuid, id, titleCache, taxonBase.name.rank from TaxonBase "  );
1871
        }
1872
        if (limit != null){
1873
           query.setMaxResults(limit);
1874
        }
1875

    
1876
        return getUuidAndTitleCache(query);
1877
    }
1878

    
1879
    @Override
1880
    protected List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCache(Query query){
1881
        List<UuidAndTitleCache<TaxonBase>> list = new ArrayList<UuidAndTitleCache<TaxonBase>>();
1882

    
1883
        List<Object[]> result = query.list();
1884
        if (result != null && !result.isEmpty()){
1885
            if (result.iterator().next().length == 4){
1886
                Collections.sort(result, new UuidAndTitleCacheTaxonComparator());
1887
            }
1888
        }
1889

    
1890
        for(Object[] object : result){
1891
            list.add(new UuidAndTitleCache<TaxonBase>((UUID) object[0],(Integer) object[1], (String) object[2]));
1892
        }
1893
        return list;
1894
    }
1895

    
1896

    
1897
}
(3-3/5)