Project

General

Profile

Download (97.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.log4j.Logger;
24
import org.hibernate.Criteria;
25
import org.hibernate.FetchMode;
26
import org.hibernate.Hibernate;
27
import org.hibernate.Query;
28
import org.hibernate.Session;
29
import org.hibernate.criterion.Criterion;
30
import org.hibernate.criterion.Order;
31
import org.hibernate.criterion.Projections;
32
import org.hibernate.criterion.Restrictions;
33
import org.hibernate.envers.query.AuditEntity;
34
import org.hibernate.envers.query.AuditQuery;
35
import org.hibernate.search.FullTextSession;
36
import org.hibernate.search.Search;
37
import org.springframework.beans.factory.annotation.Autowired;
38
import org.springframework.beans.factory.annotation.Qualifier;
39
import org.springframework.dao.DataAccessException;
40
import org.springframework.stereotype.Repository;
41

    
42
import eu.etaxonomy.cdm.model.common.CdmBase;
43
import eu.etaxonomy.cdm.model.common.DefinedTerm;
44
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
45
import eu.etaxonomy.cdm.model.common.LSID;
46
import eu.etaxonomy.cdm.model.common.OriginalSourceBase;
47
import eu.etaxonomy.cdm.model.common.RelationshipBase;
48
import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
49
import eu.etaxonomy.cdm.model.location.NamedArea;
50
import eu.etaxonomy.cdm.model.name.NonViralName;
51
import eu.etaxonomy.cdm.model.name.Rank;
52
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
53
import eu.etaxonomy.cdm.model.name.TaxonNameComparator;
54
import eu.etaxonomy.cdm.model.reference.Reference;
55
import eu.etaxonomy.cdm.model.taxon.Classification;
56
import eu.etaxonomy.cdm.model.taxon.Synonym;
57
import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
58
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
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.view.AuditEvent;
65
import eu.etaxonomy.cdm.persistence.dao.hibernate.common.IdentifiableDaoBase;
66
import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
67
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
68
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
69
import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;
70
import eu.etaxonomy.cdm.persistence.query.MatchMode;
71
import eu.etaxonomy.cdm.persistence.query.OrderHint;
72

    
73

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

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

    
92
    @Autowired
93
    private ITaxonNameDao taxonNameDao;
94

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

    
101
    @Override
102
    public List<Taxon> getRootTaxa(Reference sec) {
103
        return getRootTaxa(sec, CdmFetch.FETCH_CHILDTAXA(), true, false);
104
    }
105

    
106
    @Override
107
    public List<Taxon> getRootTaxa(Rank rank, Reference sec, CdmFetch cdmFetch, Boolean onlyWithChildren, Boolean withMisapplications, List<String> propertyPaths) {
108
        checkNotInPriorView("TaxonDaoHibernateImpl.getRootTaxa(Rank rank, Reference sec, CdmFetch cdmFetch, Boolean onlyWithChildren, Boolean withMisapplications)");
109
        if (onlyWithChildren == null){
110
            onlyWithChildren = true;
111
        }
112
        if (withMisapplications == null){
113
            withMisapplications = true;
114
        }
115
        if (cdmFetch == null){
116
            cdmFetch = CdmFetch.NO_FETCH();
117
        }
118

    
119
        Criteria crit = getSession().createCriteria(Taxon.class);
120

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

    
124
        if (rank != null) {
125
            crit.add(Restrictions.eq("name.rank", rank));
126
        }else{
127
            crit.add(Restrictions.isNull("taxonomicParentCache"));
128
        }
129

    
130
        if (sec != null){
131
            crit.add(Restrictions.eq("sec", sec) );
132
        }
133

    
134
        if (! cdmFetch.includes(CdmFetch.FETCH_CHILDTAXA())){
135
            logger.info("Not fetching child taxa");
136
            //TODO overwrite LAZY (SELECT) does not work (bug in hibernate?)
137
            crit.setFetchMode("relationsToThisTaxon.fromTaxon", FetchMode.LAZY);
138
        }
139

    
140
        List<Taxon> results = new ArrayList<Taxon>();
141
        List<Taxon> taxa = crit.list();
142
        for(Taxon taxon : taxa){
143

    
144

    
145
            //childTaxa
146
            //TODO create restriction instead
147
            // (a) not using cache fields
148
            /*Hibernate.initialize(taxon.getRelationsFromThisTaxon());
149
            if (onlyWithChildren == false || taxon.getRelationsFromThisTaxon().size() > 0){
150
                if (withMisapplications == true || ! taxon.isMisappliedName()){
151
                    defaultBeanInitializer.initialize(taxon, propertyPaths);
152
                    results.add(taxon);
153
                }
154
            }*/
155
            // (b) using cache fields
156
            if (onlyWithChildren == false || taxon.hasTaxonomicChildren()){
157
                if (withMisapplications == true || ! taxon.isMisapplication()){
158
                    defaultBeanInitializer.initialize(taxon, propertyPaths);
159
                    results.add(taxon);
160
                }
161
            }
162
        }
163
        return results;
164
    }
165

    
166
    @Override
167
    public List<Taxon> getRootTaxa(Reference sec, CdmFetch cdmFetch, Boolean onlyWithChildren, Boolean withMisapplications) {
168
        return getRootTaxa(null, sec, cdmFetch, onlyWithChildren, withMisapplications, null);
169
    }
170

    
171
    @Override
172
    public List<TaxonBase> getTaxaByName(String queryString, Reference sec) {
173

    
174
        return getTaxaByName(queryString, true, sec);
175
    }
176

    
177
    @Override
178
    public List<TaxonBase> getTaxaByName(String queryString, Boolean accepted, Reference sec) {
179
        checkNotInPriorView("TaxonDaoHibernateImpl.getTaxaByName(String name, Reference sec)");
180

    
181
        Criteria criteria = null;
182
        if (accepted == true) {
183
            criteria = getSession().createCriteria(Taxon.class);
184
        } else {
185
            criteria = getSession().createCriteria(Synonym.class);
186
        }
187

    
188
        criteria.setFetchMode( "name", FetchMode.JOIN );
189
        criteria.createAlias("name", "name");
190

    
191
        if (sec != null && sec.getId() != 0) {
192
            criteria.add(Restrictions.eq("sec", sec ) );
193
        }
194

    
195
        if (queryString != null) {
196
            criteria.add(Restrictions.ilike("name.nameCache", queryString));
197
        }
198

    
199
        return criteria.list();
200
    }
201

    
202
    public List<TaxonBase> getTaxaByName(boolean doTaxa, boolean doSynonyms, String queryString, MatchMode matchMode,
203
            Integer pageSize, Integer pageNumber) {
204

    
205
        return getTaxaByName(doTaxa, doSynonyms, false, queryString, null, matchMode, null, pageSize, pageNumber, null);
206
    }
207

    
208
    @Override
209
    public List<TaxonBase> getTaxaByName(String queryString, MatchMode matchMode,
210
            Boolean accepted, Integer pageSize, Integer pageNumber) {
211

    
212
        boolean doTaxa = true;
213
        boolean doSynonyms = true;
214

    
215
        if (accepted == true) {
216
            doSynonyms = false;
217
        } else {
218
           doTaxa = false;
219
        }
220
        return getTaxaByName(doTaxa, doSynonyms, queryString, matchMode, pageSize, pageNumber);
221
    }
222

    
223
    @Override
224
    public List<TaxonBase> getTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames,String queryString, Classification classification,
225
            MatchMode matchMode, Set<NamedArea> namedAreas, Integer pageSize,
226
            Integer pageNumber, List<String> propertyPaths) {
227

    
228
        boolean doCount = false;
229

    
230
        Query query = prepareTaxaByName(doTaxa, doSynonyms, doMisappliedNames, "nameCache", queryString, classification, matchMode, namedAreas, pageSize, pageNumber, doCount);
231

    
232
        if (query != null){
233
            @SuppressWarnings("unchecked")
234
            List<TaxonBase> results = query.list();
235

    
236
            defaultBeanInitializer.initializeAll(results, propertyPaths);
237
            //TaxonComparatorSearch comp = new TaxonComparatorSearch();
238
            //Collections.sort(results, comp);
239
            return results;
240
        }
241

    
242
        return new ArrayList<TaxonBase>();
243

    
244
    }
245

    
246

    
247
    //new search for the editor, for performance issues the return values are only uuid and titleCache, to avoid the initialisation of all objects
248
    @Override
249
    @SuppressWarnings("unchecked")
250
    public List<UuidAndTitleCache<IdentifiableEntity>> getTaxaByNameForEditor(boolean doTaxa, boolean doSynonyms, boolean doNamesWithoutTaxa, boolean doMisappliedNames, String queryString, Classification classification,
251
            MatchMode matchMode, Set<NamedArea> namedAreas) {
252
//        long zstVorher;
253
//        long zstNachher;
254

    
255
        boolean doCount = false;
256
        List<UuidAndTitleCache<IdentifiableEntity>> resultObjects = new ArrayList<UuidAndTitleCache<IdentifiableEntity>>();
257
        if (doNamesWithoutTaxa){
258
        	List<? extends TaxonNameBase<?,?>> nameResult = taxonNameDao.findByName(queryString,matchMode, null, null, null, null);
259

    
260
        	for (TaxonNameBase name: nameResult){
261
        		if (name.getTaxonBases().size() == 0){
262
        			resultObjects.add(new UuidAndTitleCache(TaxonNameBase.class, name.getUuid(), name.getId(), name.getTitleCache()));
263
        		}
264
        	}
265
        	if (!doSynonyms && !doTaxa){
266
        		return resultObjects;
267
        	}
268
        }
269
        Query query = prepareTaxaByNameForEditor(doTaxa, doSynonyms, doMisappliedNames, "nameCache", queryString, classification, matchMode, namedAreas, doCount);
270

    
271

    
272
        if (query != null){
273
            List<Object[]> results = query.list();
274

    
275
            Object[] result;
276
            for(int i = 0; i<results.size();i++){
277
                result = results.get(i);
278

    
279
                //differentiate taxa and synonyms
280
                // new Boolean(result[3].toString()) is due to the fact that result[3] could be a Boolean ora String
281
                // see FIXME in 'prepareQuery' for more details
282
                if (doTaxa && doSynonyms){
283
                    if (result[3].equals("synonym")) {
284
                        resultObjects.add( new UuidAndTitleCache(Synonym.class, (UUID) result[0], (Integer) result[1], (String)result[2], new Boolean(result[4].toString())));
285
                    }
286
                    else {
287
                        resultObjects.add( new UuidAndTitleCache(Taxon.class, (UUID) result[0], (Integer) result[1], (String)result[2], new Boolean(result[4].toString())));
288
                    }
289
                }else if (doTaxa){
290
                        resultObjects.add( new UuidAndTitleCache(Taxon.class, (UUID) result[0], (Integer) result[1], (String)result[2], new Boolean(result[4].toString())));
291
                }else if (doSynonyms){
292
                    resultObjects.add( new UuidAndTitleCache(Synonym.class, (UUID) result[0], (Integer) result[1], (String)result[2], new Boolean(result[4].toString())));
293
                }
294
            }
295

    
296

    
297

    
298
        }
299

    
300
        return resultObjects;
301

    
302
    }
303

    
304
    @Override
305
    public List<Taxon> getTaxaByCommonName(String queryString, Classification classification,
306
               MatchMode matchMode, Set<NamedArea> namedAreas, Integer pageSize,
307
               Integer pageNumber, List<String> propertyPaths) {
308
        boolean doCount = false;
309
        Query query = prepareTaxaByCommonName(queryString, classification, matchMode, namedAreas, pageSize, pageNumber, doCount, false);
310
        if (query != null){
311
            List<Taxon> results = query.list();
312
            defaultBeanInitializer.initializeAll(results, propertyPaths);
313
            return results;
314
        }
315
        return new ArrayList<Taxon>();
316

    
317
    }
318

    
319
    /**
320
     * @param clazz
321
     * @param searchField the field in TaxonNameBase to be searched through usually either <code>nameCache</code> or <code>titleCache</code>
322
     * @param queryString
323
     * @param classification TODO
324
     * @param matchMode
325
     * @param namedAreas
326
     * @param pageSize
327
     * @param pageNumber
328
     * @param doCount
329
     * @return
330
     *
331
     *
332
     */
333
    private Query prepareTaxaByNameForEditor(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, String searchField, String queryString, Classification classification,
334
            MatchMode matchMode, Set<NamedArea> namedAreas, boolean doCount) {
335
        return prepareQuery(doTaxa, doSynonyms, doMisappliedNames, searchField, queryString,
336
                classification, matchMode, namedAreas, doCount, true);
337
    }
338

    
339
    /**
340
     * @param searchField
341
     * @param queryString
342
     * @param classification
343
     * @param matchMode
344
     * @param namedAreas
345
     * @param doCount
346
     * @param doNotReturnFullEntities
347
     *            if set true the seach method will not return synonym and taxon
348
     *            entities but an array containing the uuid, titleCache, and the
349
     *            DTYPE in lowercase letters.
350
     * @param clazz
351
     * @return
352
     */
353
    private Query prepareQuery(boolean doTaxa, boolean doSynonyms, boolean doIncludeMisappliedNames, String searchField, String queryString,
354
                Classification classification, MatchMode matchMode, Set<NamedArea> namedAreas, boolean doCount, boolean doNotReturnFullEntities){
355

    
356
            String hqlQueryString = matchMode.queryStringFrom(queryString);
357
            String selectWhat;
358
            if (doNotReturnFullEntities){
359
                selectWhat = "t.uuid, t.id, t.titleCache ";
360
            }else {
361
                selectWhat = (doCount ? "count(t)": "t");
362
            }
363

    
364
            String hql = "";
365
            Set<NamedArea> areasExpanded = new HashSet<NamedArea>();
366
            if(namedAreas != null && namedAreas.size() > 0){
367
                // expand areas and restrict by distribution area
368
                Query areaQuery = getSession().createQuery("select childArea from NamedArea as childArea left join childArea.partOf as parentArea where parentArea = :area");
369
                expandNamedAreas(namedAreas, areasExpanded, areaQuery);
370
            }
371
            boolean doAreaRestriction = areasExpanded.size() > 0;
372

    
373
            Set<UUID> namedAreasUuids = new HashSet<UUID>();
374
            for (NamedArea area:areasExpanded){
375
                namedAreasUuids.add(area.getUuid());
376
            }
377

    
378

    
379
            String [] subSelects = createHQLString(doTaxa, doSynonyms, doIncludeMisappliedNames, classification, areasExpanded, matchMode, searchField);
380
            String taxonSubselect = subSelects[1];
381
            String synonymSubselect = subSelects[2];
382
            String misappliedSelect = subSelects[0];
383

    
384

    
385
            /*if(classification != null ){
386
                if (!doIncludeMisappliedNames){
387
                    if(doAreaRestriction){
388

    
389
                        taxonSubselect = "select t.id from" +
390
                            " Distribution e" +
391
                            " join e.inDescription d" +
392
                            " join d.taxon t" +
393
                            " join t.name n " +
394
                            " join t.taxonNodes as tn "+
395
                            " where" +
396
                            " e.area.uuid in (:namedAreasUuids) AND" +
397
                            " tn.classification = :classification" +
398
                            " AND n." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
399

    
400

    
401

    
402
                        synonymSubselect = "select s.id from" +
403
                            " Distribution e" +
404
                            " join e.inDescription d" +
405
                            " join d.taxon t" + // the taxa
406
                            " join t.taxonNodes as tn "+
407
                            " join t.synonymRelations sr" +
408
                            " join sr.relatedFrom s" + // the synonyms
409
                            " join s.name sn"+
410
                            " where" +
411
                            " e.area.uuid in (:namedAreasUuids) AND" +
412
                            " tn.classification = :classification" +
413
                            " AND sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
414

    
415
                    } else {
416

    
417
                        taxonSubselect = "select t.id from" +
418
                            " Taxon t" +
419
                            " join t.name n " +
420
                            " join t.taxonNodes as tn "+
421
                            " where" +
422
                            " tn.classification = :classification" +
423
                            " AND n." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
424

    
425
                        synonymSubselect = "select s.id from" +
426
                            " Taxon t" + // the taxa
427
                            " join t.taxonNodes as tn "+
428
                            " join t.synonymRelations sr" +
429
                            " join sr.relatedFrom s" + // the synonyms
430
                            " join s.name sn"+
431
                            " where" +
432
                            " tn.classification = :classification" +
433
                            " AND sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
434
                    }
435
                }else{
436
                    if(doAreaRestriction){
437
                        if (!doTaxa && !doSynonyms ){
438
                            misappliedSelect = "select t.id from" +
439
                            " Distribution e" +
440
                            " join e.inDescription d" +
441
                            " join d.taxon t" +
442
                            " join t.name n " +
443
                            " join t.taxonNodes as tn "+
444
                            " left join t.relationsFromThisTaxon as rft" +
445
                            " left join rft.relatedTo as rt" +
446
                            " left join rt.taxonNodes as tn2" +
447
                            " left join rt.name as n2" +
448
                            " left join rft.type as rtype"+
449
                            " where" +
450
                            " e.area.uuid in (:namedAreasUuids) AND" +
451
                            " (tn.classification != :classification" +
452
                            " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString" +
453
                            " AND tn2.classification = :classification" +
454
                            " AND rtype = :rType )";
455

    
456
                        }else{
457
                            taxonSubselect = "select t.id from" +
458
                                " Distribution e" +
459
                                " join e.inDescription d" +
460
                                " join d.taxon t" +
461
                                " join t.name n " +
462
                                " join t.taxonNodes as tn "+
463
                                " left join t.relationsFromThisTaxon as rft" +
464
                                " left join rft.relatedTo as rt" +
465
                                " left join rt.taxonNodes as tn2" +
466
                                " left join rt.name as n2" +
467
                                " left join rft.type as rtype"+
468
                                " where" +
469
                                " e.area.uuid in (:namedAreasUuids) AND" +
470
                                " (tn.classification = :classification" +
471
                                " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString )" +
472
                                " OR"+
473
                                " (tn.classification != :classification" +
474
                                " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString" +
475
                                " AND tn2.classification = :classification" +
476
                                " AND rtype = :rType )";
477

    
478

    
479
                            synonymSubselect = "select s.id from" +
480
                                " Distribution e" +
481
                                " join e.inDescription d" +
482
                                " join d.taxon t" + // the taxa
483
                                " join t.taxonNodes as tn "+
484
                                " join t.synonymRelations sr" +
485
                                " join sr.relatedFrom s" + // the synonyms
486
                                " join s.name sn"+
487
                                " where" +
488
                                " e.area.uuid in (:namedAreasUuids) AND" +
489
                                " tn.classification != :classification" +
490
                                " AND sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
491
                        }
492
                    } else {
493
                        if (!doTaxa && !doSynonyms ){
494
                            misappliedSelect = "select t.id from" +
495
                            " Distribution e" +
496
                            " join e.inDescription d" +
497
                            " join d.taxon t" +
498
                            " join t.name n " +
499
                            " join t.taxonNodes as tn "+
500
                            " left join t.relationsFromThisTaxon as rft" +
501
                            " left join rft.relatedTo as rt" +
502
                            " left join rt.taxonNodes as tn2" +
503
                            " left join rt.name as n2" +
504
                            " left join rft.type as rtype"+
505
                            " where" +
506
                            " (tn.classification != :classification" +
507
                            " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString" +
508
                            " AND tn2.classification = :classification" +
509
                            " AND rtype = :rType )";
510

    
511
                        }else{
512
                            taxonSubselect = "select t.id from" +
513
                                " Taxon t" +
514
                                " join t.name n " +
515
                                " join t.taxonNodes as tn "+
516
                                " left join t.relationsFromThisTaxon as rft" +
517
                                " left join rft.relatedTo as rt" +
518
                                " left join rt.taxonNodes as tn2" +
519
                                " left join rt.name as n2" +
520
                                " left join rft.type as rtype"+
521
                                " where " +
522
                                " (tn.classification = :classification" +
523
                                " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString )" +
524
                                " OR"+
525
                                " (tn.classification != :classification" +
526
                                " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString" +
527
                                " AND tn2.classification = :classification" +
528
                                " AND rtype = :rType )";
529

    
530
                            synonymSubselect = "select s.id from" +
531
                                " Taxon t" + // the taxa
532
                                " join t.taxonNodes as tn "+
533
                                " join t.synonymRelations sr" +
534
                                " join sr.relatedFrom s" + // the synonyms
535
                                " join s.name sn"+
536
                                " where" +
537
                                " tn.classification != :classification" +
538
                                " AND sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
539
                        }
540
                    }
541
                }
542
            } else {
543

    
544
                if (!doIncludeMisappliedNames){
545
                    if(doAreaRestriction){
546
                        taxonSubselect = "select t.id from " +
547
                            " Distribution e" +
548
                            " join e.inDescription d" +
549
                            " join d.taxon t" +
550
                            " join t.name n "+
551
                            " where" +
552
                            (doAreaRestriction ? " e.area.uuid in (:namedAreasUuids) AND" : "") +
553
                            " n." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
554

    
555
                        synonymSubselect = "select s.id from" +
556
                            " Distribution e" +
557
                            " join e.inDescription d" +
558
                            " join d.taxon t" + // the taxa
559
                            " join t.synonymRelations sr" +
560
                            " join sr.relatedFrom s" + // the synonyms
561
                            " join s.name sn"+
562
                            " where" +
563
                            (doAreaRestriction ? " e.area.uuid in (:namedAreasUuids) AND" : "") +
564
                            " sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
565

    
566
                    } else {
567

    
568
                        taxonSubselect = "select t.id from " +
569
                            " Taxon t" +
570
                            " join t.name n "+
571
                            " where" +
572
                            " n." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
573

    
574
                        synonymSubselect = "select s.id from" +
575
                            " Taxon t" + // the taxa
576
                            " join t.synonymRelations sr" +
577
                            " join sr.relatedFrom s" + // the synonyms
578
                            " join s.name sn"+
579
                            " where" +
580
                            " sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
581
                    }
582
                }else{
583

    
584
                }
585

    
586
            }*/
587

    
588
            logger.debug("taxonSubselect: " + taxonSubselect != null ? taxonSubselect: "NULL");
589
            logger.debug("synonymSubselect: " + synonymSubselect != null ? synonymSubselect: "NULL");
590

    
591
            Query subTaxon = null;
592
            Query subSynonym = null;
593
            Query subMisappliedNames = null;
594
            if(doTaxa){
595
                // find Taxa
596
                subTaxon = getSession().createQuery(taxonSubselect).setParameter("queryString", hqlQueryString);
597

    
598
                if(doAreaRestriction){
599
                    subTaxon.setParameterList("namedAreasUuids", namedAreasUuids);
600
                }
601
                if(classification != null){
602
                    subTaxon.setParameter("classification", classification);
603

    
604
                }
605

    
606

    
607
            }
608

    
609
            if(doSynonyms){
610
                // find synonyms
611
                subSynonym = getSession().createQuery(synonymSubselect).setParameter("queryString", hqlQueryString);
612

    
613
                if(doAreaRestriction){
614
                    subSynonym.setParameterList("namedAreasUuids", namedAreasUuids);
615
                }
616
                if(classification != null){
617
                    subSynonym.setParameter("classification", classification);
618
                }
619
            }
620
            if (doIncludeMisappliedNames ){
621
                subMisappliedNames = getSession().createQuery(misappliedSelect).setParameter("queryString", hqlQueryString);
622
                subMisappliedNames.setParameter("rType", TaxonRelationshipType.MISAPPLIED_NAME_FOR());
623
                if(doAreaRestriction){
624
                    subMisappliedNames.setParameterList("namedAreasUuids", namedAreasUuids);
625
                }
626
                if(classification != null){
627
                    subMisappliedNames.setParameter("classification", classification);
628
                }
629
            }
630

    
631
            List<Integer> taxa = new ArrayList<Integer>();
632
            List<Integer> synonyms = new ArrayList<Integer>();
633
            if (doSynonyms){
634
                synonyms = subSynonym.list();
635
            }
636
            if(doTaxa){
637
                taxa = subTaxon.list();
638
            }
639
            if (doIncludeMisappliedNames){
640
                taxa.addAll(subMisappliedNames.list());
641
            }
642

    
643
            //FIXME : the fourth element of the result should be a boolean, but in the case of a synonym
644
            // (which does require a check) a constant boolean (false) value needs to set. It seems that
645
            // hql cannot parse a constant boolean value in the select list clause. This implies that the
646
            // resulting object could be a Boolean or a String. The workaround for this is to convert the
647
            // resutling object into a String (using toString) and then create a new Boolean object from
648
            // String.
649
            if (doTaxa && doSynonyms){
650
                if(synonyms.size()>0 && taxa.size()>0){
651
                    hql = "select " + selectWhat;
652
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
653
                    // also return the computed isOrphaned flag
654
                    if (doNotReturnFullEntities &&  !doCount ){
655
                        hql += ", case when t.id in (:taxa) then 'taxon' else 'synonym' end, " +
656
                                " 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 ";
657
                    }
658
                    hql +=  " from %s t " +
659
                            " where (t.id in (:taxa) OR t.id in (:synonyms)) ";
660
                }else if (synonyms.size()>0 ){
661
                    hql = "select " + selectWhat;
662
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
663
                    // also return the computed isOrphaned flag
664
                    if (doNotReturnFullEntities &&  !doCount ){
665
                        hql += ", 'synonym', 'false' ";
666

    
667
                    }
668
                    hql +=  " from %s t " +
669
                            " where t.id in (:synonyms) ";
670

    
671
                } else if (taxa.size()>0 ){
672
                    hql = "select " + selectWhat;
673
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
674
                    // also return the computed isOrphaned flag
675
                    if (doNotReturnFullEntities &&  !doCount ){
676
                        hql += ", 'taxon', " +
677
                                " case when t.taxonNodes is empty and t.relationsFromThisTaxon is empty and t.relationsToThisTaxon is empty then true else false end ";
678
                    }
679
                    hql +=  " from %s t " +
680
                            " where t.id in (:taxa) ";
681

    
682
                } else{
683
                    hql = "select " + selectWhat + " from %s t";
684
                }
685
            } else if(doTaxa){
686
                if  (taxa.size()>0){
687
                    hql = "select " + selectWhat;
688
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
689
                    // also return the computed isOrphaned flag
690
                    if (doNotReturnFullEntities){
691
                        hql += ", 'taxon', " +
692
                                " case when t.taxonNodes is empty and t.relationsFromThisTaxon is empty and t.relationsToThisTaxon is empty then true else false end ";
693
                    }
694
                    hql +=  " from %s t " +
695
                            " where t.id in (:taxa) ";
696

    
697
                }else{
698
                    hql = "select " + selectWhat + " from %s t";
699
                }
700
            } else if(doSynonyms){
701
                if (synonyms.size()>0){
702

    
703
                    hql = "select " + selectWhat;
704
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
705
                    // also return the computed isOrphaned flag
706
                    if (doNotReturnFullEntities){
707
                        hql += ", 'synonym', 'false' ";
708
                    }
709
                    hql +=  " from %s t " +
710
                            " where t.id in (:synonyms) ";
711
                }else{
712
                    hql = "select " + selectWhat + " from %s t";
713
                }
714
            } else if (doIncludeMisappliedNames){
715
                hql = "select " + selectWhat;
716
                // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
717
                // also return the computed isOrphaned flag
718
                if (doNotReturnFullEntities){
719
                    hql += ", 'taxon', " +
720
                            " case when t.taxonNodes is empty and t.relationsFromThisTaxon is empty and t.relationsToThisTaxon is empty then true else false end ";
721
                }
722
                hql +=  " from %s t " +
723
                        " where t.id in (:taxa) ";
724

    
725
            }
726

    
727
            String classString;
728
            if (doTaxa && doSynonyms){
729
                classString = "TaxonBase";
730
            } else if (doTaxa){
731
                classString = "Taxon";
732
            } else if (doSynonyms){
733
                classString = "Synonym";
734
            } else{//only misappliedNames
735
                classString = "Taxon";
736
            }
737

    
738
            hql = String.format(hql, classString);
739

    
740

    
741
            if (hql == "") {
742
                return null;
743
            }
744
            if(!doCount){
745
                hql += " order by t.name.genusOrUninomial, case when t.name.specificEpithet like '\"%\"' then 1 else 0 end, t.name.specificEpithet, t.name.rank desc, t.name.nameCache";
746
            }
747

    
748
            logger.debug("hql: " + hql);
749
            Query query = getSession().createQuery(hql);
750

    
751

    
752
            if (doTaxa && doSynonyms){
753
                // find taxa and synonyms
754
                if (taxa.size()>0){
755
                    query.setParameterList("taxa", taxa);
756
                }
757
                if (synonyms.size()>0){
758
                    query.setParameterList("synonyms",synonyms);
759
                }
760
                if (taxa.size()== 0 && synonyms.size() == 0){
761
                    return null;
762
                }
763
            }else if(doTaxa){
764
                //find taxa
765
                if (taxa.size()>0){
766
                    query.setParameterList("taxa", taxa );
767
                }else{
768
                    logger.warn("there are no taxa for the query: " + queryString);
769
                    return null;
770
                }
771
            } else if(doSynonyms){
772
                // find synonyms
773
                if (synonyms.size()>0){
774
                    query.setParameterList("synonyms", synonyms);
775
                }else{
776
                    return null;
777
                }
778
            }	else{
779
                //only misappliedNames
780
                if (taxa.size()>0){
781
                    query.setParameterList("taxa", taxa );
782
                }else{
783
                    return null;
784
                }
785
            }
786

    
787
            return query;
788
    }
789

    
790

    
791
    /**
792
     * @param searchField the field in TaxonNameBase to be searched through usually either <code>nameCache</code> or <code>titleCache</code>
793
     * @param queryString
794
     * @param classification TODO
795
     * @param matchMode
796
     * @param namedAreas
797
     * @param pageSize
798
     * @param pageNumber
799
     * @param doCount
800
     * @param clazz
801
     * @return
802
     *
803
     * FIXME implement classification restriction & implement test: see {@link TaxonDaoHibernateImplTest#testCountTaxaByName()}
804
     */
805
    private Query prepareTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, String searchField, String queryString,
806
            Classification classification, MatchMode matchMode, Set<NamedArea> namedAreas, Integer pageSize, Integer pageNumber, boolean doCount) {
807

    
808
        Query query = prepareQuery(doTaxa, doSynonyms, doMisappliedNames, searchField, queryString, classification, matchMode, namedAreas, doCount, false);
809

    
810
        if(pageSize != null &&  !doCount && query != null) {
811
            query.setMaxResults(pageSize);
812
            if(pageNumber != null) {
813
                query.setFirstResult(pageNumber * pageSize);
814
            }
815
        }
816

    
817
        return query;
818
    }
819

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

    
823
        String what = "select";
824
        if (doNotReturnFullEntities){
825
        	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 ";
826
        }else {
827
        	what += (doCount ? " count(t)": " t");
828
        }
829
        String hql= what + " from Taxon t " +
830
        "join t.descriptions d "+
831
        "join d.descriptionElements e " +
832
        "join e.feature f " +
833
        "where f.supportsCommonTaxonName = true and e.name "+matchMode.getMatchOperator()+" :queryString";//and ls.text like 'common%'";
834

    
835
        Query query = getSession().createQuery(hql);
836

    
837
        query.setParameter("queryString", matchMode.queryStringFrom(queryString));
838

    
839
        if(pageSize != null &&  !doCount) {
840
            query.setMaxResults(pageSize);
841
            if(pageNumber != null) {
842
                query.setFirstResult(pageNumber * pageSize);
843
            }
844
        }
845
        return query;
846
    }
847

    
848
    @Override
849
    public long countTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, String queryString, Classification classification,
850
        MatchMode matchMode, Set<NamedArea> namedAreas) {
851

    
852
        boolean doCount = true;
853
        /*
854
        boolean doTaxa = true;
855
        boolean doSynonyms = true;
856
        if (clazz.equals(Taxon.class)){
857
            doSynonyms = false;
858
        } else if (clazz.equals(Synonym.class)){
859
            doTaxa = false;
860
        }
861
        */
862

    
863

    
864
        Query query = prepareTaxaByName(doTaxa, doSynonyms, doMisappliedNames, "nameCache", queryString, classification, matchMode, namedAreas, null, null, doCount);
865
        if (query != null) {
866
            return (Long)query.uniqueResult();
867
        }else{
868
            return 0;
869
        }
870
    }
871

    
872
    /**
873
     * @param namedAreas
874
     * @param areasExpanded
875
     * @param areaQuery
876
     */
877
    private void expandNamedAreas(Collection<NamedArea> namedAreas, Set<NamedArea> areasExpanded, Query areaQuery) {
878
        List<NamedArea> childAreas;
879
        for(NamedArea a : namedAreas){
880
            areasExpanded.add(a);
881
            areaQuery.setParameter("area", a);
882
            childAreas = areaQuery.list();
883
            if(childAreas.size() > 0){
884
                areasExpanded.addAll(childAreas);
885
                expandNamedAreas(childAreas, areasExpanded, areaQuery);
886
            }
887
        }
888
    }
889

    
890

    
891
    @Override
892
    public List<TaxonBase> getAllTaxonBases(Integer pagesize, Integer page) {
893
        return super.list(pagesize, page);
894
    }
895

    
896
    @Override
897
    public List<Synonym> getAllSynonyms(Integer limit, Integer start) {
898
        Criteria criteria = getSession().createCriteria(Synonym.class);
899

    
900
        if(limit != null) {
901
            criteria.setFirstResult(start);
902
            criteria.setMaxResults(limit);
903
        }
904

    
905
        return criteria.list();
906
    }
907

    
908
    @Override
909
    public List<Taxon> getAllTaxa(Integer limit, Integer start) {
910
        Criteria criteria = getSession().createCriteria(Taxon.class);
911

    
912
        if(limit != null) {
913
            criteria.setFirstResult(start);
914
            criteria.setMaxResults(limit);
915
        }
916

    
917
        return criteria.list();
918
    }
919

    
920
    @Override
921
    public List<RelationshipBase> getAllRelationships(/*Class<? extends RelationshipBase> clazz,*/ Integer limit, Integer start) {
922
        Class<? extends RelationshipBase> clazz = RelationshipBase.class;  //preliminary, see #2653
923
        AuditEvent auditEvent = getAuditEventFromContext();
924
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
925
            // for some reason the HQL .class discriminator didn't work here so I created this preliminary
926
            // implementation for now. Should be cleaned in future.
927

    
928
            List<RelationshipBase> result = new ArrayList<RelationshipBase>();
929

    
930
            int taxRelSize = countAllRelationships(TaxonRelationship.class);
931

    
932
            if (taxRelSize > start){
933

    
934
                String hql = " FROM TaxonRelationship as rb ORDER BY rb.id ";
935
                Query query = getSession().createQuery(hql);
936
                query.setFirstResult(start);
937
                if (limit != null){
938
                    query.setMaxResults(limit);
939
                }
940
                result = query.list();
941
            }
942
            limit = limit - result.size();
943
            if (limit > 0){
944
                String hql = " FROM SynonymRelationship as rb ORDER BY rb.id ";
945
                Query query = getSession().createQuery(hql);
946
                start = (taxRelSize > start) ? 0 : (start - taxRelSize);
947
                query.setFirstResult(start);
948
                if (limit != null){
949
                    query.setMaxResults(limit);
950
                }
951
                result.addAll(query.list());
952
            }
953
            return result;
954

    
955
        } else {
956
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(clazz,auditEvent.getRevisionNumber());
957
            return query.getResultList();
958
        }
959
    }
960

    
961
    @Override
962
    public UUID delete(TaxonBase taxonBase) throws DataAccessException{
963
        if (taxonBase == null){
964
            logger.warn("TaxonBase was 'null'");
965
            return null;
966
        }
967

    
968
        // Merge the object in if it is detached
969
        //
970
        // I think this is preferable to catching lazy initialization errors
971
        // as that solution only swallows and hides the exception, but doesn't
972
        // actually solve it.
973
        getSession().merge(taxonBase);
974

    
975
        taxonBase.removeSources();
976

    
977
        if (taxonBase instanceof Taxon){ //	is Taxon
978
            for (Iterator<TaxonRelationship> iterator = ((Taxon)taxonBase).getRelationsFromThisTaxon().iterator(); iterator.hasNext();){
979
                TaxonRelationship relationFromThisTaxon = iterator.next();
980

    
981
                // decrease children count of taxonomic parent by one
982
                if (relationFromThisTaxon.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())) {
983
                    Taxon toTaxon = relationFromThisTaxon.getToTaxon(); // parent
984
                    if (toTaxon != null) {
985
                        toTaxon.setTaxonomicChildrenCount(toTaxon.getTaxonomicChildrenCount() - 1);
986
                    }
987
                }
988
            }
989
        }
990

    
991
       return super.delete(taxonBase);
992

    
993
    }
994

    
995
    @Override
996
    public List<TaxonBase> findByNameTitleCache(boolean doTaxa, boolean doSynonyms, String queryString, Classification classification, MatchMode matchMode, Set<NamedArea> namedAreas, Integer pageNumber, Integer pageSize, List<String> propertyPaths) {
997

    
998
        boolean doCount = false;
999
        Query query = prepareTaxaByName(doTaxa, doSynonyms, false, "titleCache", queryString, classification, matchMode, namedAreas, pageSize, pageNumber, doCount);
1000
        if (query != null){
1001
            List<TaxonBase> results = query.list();
1002
            defaultBeanInitializer.initializeAll(results, propertyPaths);
1003
            return results;
1004
        }
1005
        return new ArrayList<TaxonBase>();
1006

    
1007
    }
1008

    
1009
    @Override
1010
    public TaxonBase findByUuid(UUID uuid, List<Criterion> criteria, List<String> propertyPaths) {
1011

    
1012
        Criteria crit = getSession().createCriteria(type);
1013

    
1014
        if (uuid != null) {
1015
            crit.add(Restrictions.eq("uuid", uuid));
1016
        } else {
1017
            logger.warn("UUID is NULL");
1018
            return null;
1019
        }
1020
        if(criteria != null){
1021
            for (Criterion criterion : criteria) {
1022
                crit.add(criterion);
1023
            }
1024
        }
1025
        crit.addOrder(Order.asc("uuid"));
1026

    
1027
        List<? extends TaxonBase> results = crit.list();
1028
        if (results.size() == 1) {
1029
            defaultBeanInitializer.initializeAll(results, propertyPaths);
1030
            TaxonBase taxon = results.iterator().next();
1031
            return taxon;
1032
        } else if (results.size() > 1) {
1033
            logger.error("Multiple results for UUID: " + uuid);
1034
        } else if (results.size() == 0) {
1035
            logger.info("No results for UUID: " + uuid);
1036
        }
1037

    
1038
        return null;
1039
    }
1040

    
1041
    @Override
1042
    public List<? extends TaxonBase> findByUuids(List<UUID> uuids, List<Criterion> criteria, List<String> propertyPaths) {
1043

    
1044
        Criteria crit = getSession().createCriteria(type);
1045

    
1046
        if (uuids != null) {
1047
            crit.add(Restrictions.in("uuid", uuids));
1048
        } else {
1049
            logger.warn("List<UUID> uuids is NULL");
1050
            return null;
1051
        }
1052
        if(criteria != null){
1053
            for (Criterion criterion : criteria) {
1054
                crit.add(criterion);
1055
            }
1056
        }
1057
        crit.addOrder(Order.asc("uuid"));
1058

    
1059
        List<? extends TaxonBase> results = crit.list();
1060

    
1061
        defaultBeanInitializer.initializeAll(results, propertyPaths);
1062
        return results;
1063
    }
1064

    
1065
    @Override
1066
    public int countMatchesByName(String queryString, MatchMode matchMode, boolean onlyAcccepted) {
1067
        checkNotInPriorView("TaxonDaoHibernateImpl.countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted)");
1068
        Criteria crit = getSession().createCriteria(type);
1069
        crit.add(Restrictions.ilike("titleCache", matchMode.queryStringFrom(queryString)));
1070
        crit.setProjection(Projections.rowCount());
1071
        int result = ((Number)crit.list().get(0)).intValue();
1072
        return result;
1073
    }
1074

    
1075

    
1076
    @Override
1077
    public int countMatchesByName(String queryString, MatchMode matchMode, boolean onlyAcccepted, List<Criterion> criteria) {
1078
        checkNotInPriorView("TaxonDaoHibernateImpl.countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted, List<Criterion> criteria)");
1079
        Criteria crit = getSession().createCriteria(type);
1080
        crit.add(Restrictions.ilike("titleCache", matchMode.queryStringFrom(queryString)));
1081
        if(criteria != null){
1082
            for (Criterion criterion : criteria) {
1083
                crit.add(criterion);
1084
            }
1085
        }
1086
        crit.setProjection(Projections.rowCount());
1087
        int result = ((Number)crit.list().get(0)).intValue();
1088
        return result;
1089
    }
1090

    
1091
    @Override
1092
    public int countTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Direction direction) {
1093
        AuditEvent auditEvent = getAuditEventFromContext();
1094
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1095
            Query query = null;
1096

    
1097
            if(type == null) {
1098
                query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon");
1099
            } else {
1100
                query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon and taxonRelationship.type = :type");
1101
                query.setParameter("type",type);
1102
            }
1103
            query.setParameter("relatedTaxon", taxon);
1104

    
1105
            return ((Long)query.uniqueResult()).intValue();
1106
        } else {
1107
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1108
            query.add(AuditEntity.relatedId(direction.toString()).eq(taxon.getId()));
1109
            query.addProjection(AuditEntity.id().count());
1110

    
1111
            if(type != null) {
1112
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1113
            }
1114

    
1115
            return ((Long)query.getSingleResult()).intValue();
1116
        }
1117
    }
1118

    
1119
    @Override
1120
    public int countSynonyms(Taxon taxon, SynonymRelationshipType type) {
1121
        AuditEvent auditEvent = getAuditEventFromContext();
1122
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1123
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1124

    
1125
            criteria.add(Restrictions.eq("relatedTo", taxon));
1126
            if(type != null) {
1127
                criteria.add(Restrictions.eq("type", type));
1128
            }
1129
            criteria.setProjection(Projections.rowCount());
1130
            return ((Number)criteria.uniqueResult()).intValue();
1131
        } else {
1132
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());
1133
            query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));
1134
            query.addProjection(AuditEntity.id().count());
1135

    
1136
            if(type != null) {
1137
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1138
            }
1139

    
1140
            return ((Long)query.getSingleResult()).intValue();
1141
        }
1142
    }
1143

    
1144
    @Override
1145
    public int countSynonyms(Synonym synonym, SynonymRelationshipType type) {
1146
        AuditEvent auditEvent = getAuditEventFromContext();
1147
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1148
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1149

    
1150
            criteria.add(Restrictions.eq("relatedFrom", synonym));
1151
            if(type != null) {
1152
                criteria.add(Restrictions.eq("type", type));
1153
            }
1154

    
1155
            criteria.setProjection(Projections.rowCount());
1156
            return ((Number)criteria.uniqueResult()).intValue();
1157
        } else {
1158
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());
1159
            query.add(AuditEntity.relatedId("relatedFrom").eq(synonym.getId()));
1160
            query.addProjection(AuditEntity.id().count());
1161

    
1162
            if(type != null) {
1163
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1164
            }
1165

    
1166
            return ((Long)query.getSingleResult()).intValue();
1167
        }
1168
    }
1169

    
1170
    @Override
1171
    public int countTaxaByName(Class<? extends TaxonBase> clazz, String genusOrUninomial, String infraGenericEpithet, String specificEpithet,	String infraSpecificEpithet, Rank rank) {
1172
        checkNotInPriorView("TaxonDaoHibernateImpl.countTaxaByName(Boolean accepted, String genusOrUninomial,	String infraGenericEpithet, String specificEpithet,	String infraSpecificEpithet, Rank rank)");
1173
        Criteria criteria = null;
1174

    
1175
        criteria = getSession().createCriteria(clazz);
1176

    
1177
        criteria.setFetchMode( "name", FetchMode.JOIN );
1178
        criteria.createAlias("name", "name");
1179

    
1180
        if(genusOrUninomial == null) {
1181
            criteria.add(Restrictions.isNull("name.genusOrUninomial"));
1182
        } else if(!genusOrUninomial.equals("*")) {
1183
            criteria.add(Restrictions.eq("name.genusOrUninomial", genusOrUninomial));
1184
        }
1185

    
1186
        if(infraGenericEpithet == null) {
1187
            criteria.add(Restrictions.isNull("name.infraGenericEpithet"));
1188
        } else if(!infraGenericEpithet.equals("*")) {
1189
            criteria.add(Restrictions.eq("name.infraGenericEpithet", infraGenericEpithet));
1190
        }
1191

    
1192
        if(specificEpithet == null) {
1193
            criteria.add(Restrictions.isNull("name.specificEpithet"));
1194
        } else if(!specificEpithet.equals("*")) {
1195
            criteria.add(Restrictions.eq("name.specificEpithet", specificEpithet));
1196

    
1197
        }
1198

    
1199
        if(infraSpecificEpithet == null) {
1200
            criteria.add(Restrictions.isNull("name.infraSpecificEpithet"));
1201
        } else if(!infraSpecificEpithet.equals("*")) {
1202
            criteria.add(Restrictions.eq("name.infraSpecificEpithet", infraSpecificEpithet));
1203
        }
1204

    
1205
        if(rank != null) {
1206
            criteria.add(Restrictions.eq("name.rank", rank));
1207
        }
1208

    
1209
        criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
1210

    
1211
        return ((Number)criteria.uniqueResult()).intValue();
1212
    }
1213

    
1214
    @Override
1215
    public List<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, Rank rank, Integer pageSize,	Integer pageNumber) {
1216
        checkNotInPriorView("TaxonDaoHibernateImpl.findTaxaByName(Boolean accepted, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, Rank rank, Integer pageSize,	Integer pageNumber)");
1217
        Criteria criteria = null;
1218
        if (clazz == null){
1219
            criteria = getSession().createCriteria(TaxonBase.class);
1220
        } else{
1221
            criteria = getSession().createCriteria(clazz);
1222
        }
1223
        criteria.setFetchMode( "name", FetchMode.JOIN );
1224
        criteria.createAlias("name", "name");
1225

    
1226
        if(genusOrUninomial == null) {
1227
            criteria.add(Restrictions.isNull("name.genusOrUninomial"));
1228
        } else if(!genusOrUninomial.equals("*")) {
1229
            criteria.add(Restrictions.eq("name.genusOrUninomial", genusOrUninomial));
1230
        }
1231

    
1232
        if(infraGenericEpithet == null) {
1233
            criteria.add(Restrictions.isNull("name.infraGenericEpithet"));
1234
        } else if(!infraGenericEpithet.equals("*")) {
1235
            criteria.add(Restrictions.eq("name.infraGenericEpithet", infraGenericEpithet));
1236
        }
1237

    
1238
        if(specificEpithet == null) {
1239
            criteria.add(Restrictions.isNull("name.specificEpithet"));
1240
        } else if(!specificEpithet.equals("*")) {
1241
            criteria.add(Restrictions.eq("name.specificEpithet", specificEpithet));
1242

    
1243
        }
1244

    
1245
        if(infraSpecificEpithet == null) {
1246
            criteria.add(Restrictions.isNull("name.infraSpecificEpithet"));
1247
        } else if(!infraSpecificEpithet.equals("*")) {
1248
            criteria.add(Restrictions.eq("name.infraSpecificEpithet", infraSpecificEpithet));
1249
        }
1250

    
1251
        if(rank != null) {
1252
            criteria.add(Restrictions.eq("name.rank", rank));
1253
        }
1254

    
1255
        if(pageSize != null) {
1256
            criteria.setMaxResults(pageSize);
1257
            if(pageNumber != null) {
1258
                criteria.setFirstResult(pageNumber * pageSize);
1259
            } else {
1260
                criteria.setFirstResult(0);
1261
            }
1262
        }
1263

    
1264
        return criteria.list();
1265
    }
1266

    
1267
    @Override
1268
    public List<TaxonRelationship> getTaxonRelationships(Taxon taxon, TaxonRelationshipType type,
1269
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {
1270

    
1271
        AuditEvent auditEvent = getAuditEventFromContext();
1272
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1273

    
1274
            Criteria criteria = getSession().createCriteria(TaxonRelationship.class);
1275

    
1276
            if(direction != null) {
1277
                criteria.add(Restrictions.eq(direction.name(), taxon));
1278
            } else {
1279
                criteria.add(Restrictions.or(
1280
                        Restrictions.eq(Direction.relatedFrom.name(), taxon),
1281
                        Restrictions.eq(Direction.relatedTo.name(), taxon))
1282
                    );
1283
            }
1284

    
1285
            if(type != null) {
1286
                criteria.add(Restrictions.eq("type", type));
1287
            }
1288

    
1289
            addOrder(criteria,orderHints);
1290

    
1291
            if(pageSize != null) {
1292
                criteria.setMaxResults(pageSize);
1293
                if(pageNumber != null) {
1294
                    criteria.setFirstResult(pageNumber * pageSize);
1295
                } else {
1296
                    criteria.setFirstResult(0);
1297
                }
1298
            }
1299

    
1300
            List<TaxonRelationship> result = criteria.list();
1301
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1302

    
1303
            return result;
1304
        } else {
1305
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1306
            query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));
1307

    
1308
            if(type != null) {
1309
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1310
            }
1311

    
1312
            if(pageSize != null) {
1313
                query.setMaxResults(pageSize);
1314
                if(pageNumber != null) {
1315
                    query.setFirstResult(pageNumber * pageSize);
1316
                } else {
1317
                    query.setFirstResult(0);
1318
                }
1319
            }
1320

    
1321
            List<TaxonRelationship> result = query.getResultList();
1322
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1323

    
1324
            // Ugly, but for now, there is no way to sort on a related entity property in Envers,
1325
            // and we can't live without this functionality in CATE as it screws up the whole
1326
            // taxon tree thing
1327
            if(orderHints != null && !orderHints.isEmpty()) {
1328
                SortedSet<TaxonRelationship> sortedList = new TreeSet<TaxonRelationship>(new TaxonRelationshipFromTaxonComparator());
1329
                sortedList.addAll(result);
1330
                return new ArrayList<TaxonRelationship>(sortedList);
1331
            }
1332

    
1333
            return result;
1334
        }
1335
    }
1336

    
1337
    class TaxonRelationshipFromTaxonComparator implements Comparator<TaxonRelationship> {
1338

    
1339
        @Override
1340
        public int compare(TaxonRelationship o1, TaxonRelationship o2) {
1341
            return o1.getFromTaxon().getTitleCache().compareTo(o2.getFromTaxon().getTitleCache());
1342
        }
1343

    
1344
    }
1345

    
1346
    class SynonymRelationshipFromTaxonComparator implements Comparator<SynonymRelationship> {
1347

    
1348
        @Override
1349
        public int compare(SynonymRelationship o1, SynonymRelationship o2) {
1350
            return o1.getSynonym().getTitleCache().compareTo(o2.getSynonym().getTitleCache());
1351
        }
1352

    
1353
    }
1354

    
1355
    @Override
1356
    public List<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
1357
        AuditEvent auditEvent = getAuditEventFromContext();
1358
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1359
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1360

    
1361
            criteria.add(Restrictions.eq("relatedTo", taxon));
1362
            if(type != null) {
1363
                criteria.add(Restrictions.eq("type", type));
1364
            }
1365

    
1366
            addOrder(criteria,orderHints);
1367

    
1368
            if(pageSize != null) {
1369
                criteria.setMaxResults(pageSize);
1370
                if(pageNumber != null) {
1371
                    criteria.setFirstResult(pageNumber * pageSize);
1372
                } else {
1373
                    criteria.setFirstResult(0);
1374
                }
1375
            }
1376

    
1377
            List<SynonymRelationship> result = criteria.list();
1378
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1379

    
1380
            return result;
1381
        } else {
1382
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());
1383
            query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));
1384

    
1385
            if(type != null) {
1386
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1387
            }
1388

    
1389
            if(pageSize != null) {
1390
                query.setMaxResults(pageSize);
1391
                if(pageNumber != null) {
1392
                    query.setFirstResult(pageNumber * pageSize);
1393
                } else {
1394
                    query.setFirstResult(0);
1395
                }
1396
            }
1397

    
1398
            List<SynonymRelationship> result = query.getResultList();
1399
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1400

    
1401
            return result;
1402
        }
1403
    }
1404

    
1405
    @Override
1406
    public List<SynonymRelationship> getSynonyms(Synonym synonym, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
1407
        AuditEvent auditEvent = getAuditEventFromContext();
1408
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1409
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1410

    
1411
            criteria.add(Restrictions.eq("relatedFrom", synonym));
1412
            if(type != null) {
1413
                criteria.add(Restrictions.eq("type", type));
1414
            }
1415

    
1416
            addOrder(criteria,orderHints);
1417

    
1418
            if(pageSize != null) {
1419
                criteria.setMaxResults(pageSize);
1420
                if(pageNumber != null) {
1421
                    criteria.setFirstResult(pageNumber * pageSize);
1422
                } else {
1423
                    criteria.setFirstResult(0);
1424
                }
1425
            }
1426

    
1427
            List<SynonymRelationship> result = criteria.list();
1428
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1429

    
1430
            return result;
1431
        } else {
1432
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());
1433
            query.add(AuditEntity.relatedId("relatedFrom").eq(synonym.getId()));
1434

    
1435
            if(type != null) {
1436
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1437
            }
1438

    
1439
            if(pageSize != null) {
1440
                query.setMaxResults(pageSize);
1441
                if(pageNumber != null) {
1442
                    query.setFirstResult(pageNumber * pageSize);
1443
                } else {
1444
                    query.setFirstResult(0);
1445
                }
1446
            }
1447

    
1448
            List<SynonymRelationship> result = query.getResultList();
1449
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1450

    
1451
            return result;
1452
        }
1453
    }
1454

    
1455
    @Override
1456
    public void rebuildIndex() {
1457
        FullTextSession fullTextSession = Search.getFullTextSession(getSession());
1458

    
1459
        for(TaxonBase taxonBase : list(null,null)) { // re-index all taxon base
1460
            Hibernate.initialize(taxonBase.getName());
1461
            fullTextSession.index(taxonBase);
1462
        }
1463
        fullTextSession.flushToIndexes();
1464
    }
1465

    
1466
    @Override
1467
    public String suggestQuery(String queryString) {
1468
        throw new RuntimeException("Query suggestion currently not implemented in TaxonDaoHibernateImpl");
1469
//        checkNotInPriorView("TaxonDaoHibernateImpl.suggestQuery(String queryString)");
1470
//        String alternativeQueryString = null;
1471
//        if (alternativeSpellingSuggestionParser != null) {
1472
//            try {
1473
//
1474
//                alternativeSpellingSuggestionParser.parse(queryString);
1475
//                org.apache.lucene.search.Query alternativeQuery = alternativeSpellingSuggestionParser.suggest(queryString);
1476
//                if (alternativeQuery != null) {
1477
//                    alternativeQueryString = alternativeQuery
1478
//                            .toString("name.titleCache");
1479
//                }
1480
//
1481
//            } catch (ParseException e) {
1482
//                throw new QueryParseException(e, queryString);
1483
//            }
1484
//        }
1485
//        return alternativeQueryString;
1486
    }
1487

    
1488
    @Override
1489
    public List<Taxon> listAcceptedTaxaFor(Synonym synonym, Classification classificationFilter, Integer pageSize, Integer pageNumber,
1490
            List<OrderHint> orderHints, List<String> propertyPaths){
1491

    
1492
        String hql = prepareListAcceptedTaxaFor(classificationFilter, orderHints, false);
1493

    
1494
        Query query = getSession().createQuery(hql);
1495

    
1496
        query.setParameter("synonym", synonym);
1497

    
1498
        if(classificationFilter != null){
1499
            query.setParameter("classificationFilter", classificationFilter);
1500
        }
1501

    
1502

    
1503
        if(pageSize != null) {
1504
            query.setMaxResults(pageSize);
1505
            if(pageNumber != null) {
1506
                query.setFirstResult(pageNumber * pageSize);
1507
            }
1508
        }
1509

    
1510
        @SuppressWarnings("unchecked")
1511
        List<Taxon> result = query.list();
1512

    
1513
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1514

    
1515
        return result;
1516

    
1517
    }
1518

    
1519
    @Override
1520
    public long countAcceptedTaxaFor(Synonym synonym, Classification classificationFilter){
1521

    
1522
        String hql = prepareListAcceptedTaxaFor(classificationFilter, null, true);
1523

    
1524
        Query query = getSession().createQuery(hql);
1525

    
1526
        query.setParameter("synonym", synonym);
1527

    
1528
        if(classificationFilter != null){
1529
            query.setParameter("classificationFilter", classificationFilter);
1530
        }
1531

    
1532
        Long count = Long.parseLong(query.uniqueResult().toString());
1533

    
1534
        return count;
1535

    
1536
    }
1537

    
1538

    
1539
    /**
1540
     * @param classificationFilter
1541
     * @param orderHints
1542
     * @return
1543
     */
1544
    private String prepareListAcceptedTaxaFor(Classification classificationFilter, List<OrderHint> orderHints, boolean doCount) {
1545

    
1546
        String hql;
1547
        String hqlSelect = "select " + (doCount? "count(taxon)" : "taxon") + " from Taxon as taxon left join taxon.synonymRelations as synRel ";
1548
        String hqlWhere = " where synRel.relatedFrom = :synonym";
1549

    
1550
        if(classificationFilter != null){
1551
            hqlSelect += " left join taxon.taxonNodes AS taxonNode";
1552
            hqlWhere += " and taxonNode.classification = :classificationFilter";
1553
        }
1554
        hql = hqlSelect + hqlWhere + orderByClause(orderHints, "taxon");
1555
        return hql;
1556
    }
1557

    
1558

    
1559
    @Override
1560
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, List<UUID> excludeUuid) {
1561

    
1562
        int classificationId = classification.getId();
1563
       // StringBuffer excludeUuids = new StringBuffer();
1564

    
1565

    
1566
        String queryString = "SELECT nodes.uuid, nodes.id, taxa.titleCache FROM TaxonNode AS nodes, Taxon AS taxa WHERE nodes.taxon = taxa AND nodes.classification.id = " + classificationId ;
1567
        @SuppressWarnings("unchecked")
1568
        List<Object[]> result;
1569
        if ( excludeUuid != null &&  !excludeUuid.isEmpty()){
1570
            queryString = queryString + " AND taxa.uuid NOT IN (:excludeUuid)" ;
1571

    
1572
            result = getSession().createQuery(queryString).setParameterList("excludeUuid", excludeUuid).list();
1573
        }else{
1574

    
1575
            result = getSession().createQuery(queryString).list();
1576
        }
1577

    
1578

    
1579

    
1580

    
1581
        if(result.size() == 0){
1582
            return null;
1583
        }else{
1584
            List<UuidAndTitleCache<TaxonNode>> list = new ArrayList<UuidAndTitleCache<TaxonNode>>(result.size());
1585

    
1586
            for (Object object : result){
1587

    
1588
                Object[] objectArray = (Object[]) object;
1589

    
1590
                UUID uuid = (UUID)objectArray[0];
1591
                Integer id = (Integer) objectArray[1];
1592
                String titleCache = (String) objectArray[2];
1593

    
1594
                list.add(new UuidAndTitleCache<TaxonNode>(TaxonNode.class, uuid, id, titleCache));
1595
            }
1596

    
1597
            return list;
1598
        }
1599
    }
1600

    
1601

    
1602
    @Override
1603
    public TaxonBase find(LSID lsid) {
1604
        TaxonBase<?> taxonBase = super.find(lsid);
1605
        if(taxonBase != null) {
1606
            List<String> propertyPaths = new ArrayList<String>();
1607
            propertyPaths.add("createdBy");
1608
            propertyPaths.add("updatedBy");
1609
            propertyPaths.add("name");
1610
            propertyPaths.add("sec");
1611
            propertyPaths.add("relationsToThisTaxon");
1612
            propertyPaths.add("relationsToThisTaxon.fromTaxon");
1613
            propertyPaths.add("relationsToThisTaxon.toTaxon");
1614
            propertyPaths.add("relationsFromThisTaxon");
1615
            propertyPaths.add("relationsFromThisTaxon.toTaxon");
1616
            propertyPaths.add("relationsToThisTaxon.type");
1617
            propertyPaths.add("synonymRelations");
1618
            propertyPaths.add("synonymRelations.synonym");
1619
            propertyPaths.add("synonymRelations.type");
1620
            propertyPaths.add("descriptions");
1621

    
1622
            defaultBeanInitializer.initialize(taxonBase, propertyPaths);
1623
        }
1624
        return taxonBase;
1625
    }
1626

    
1627
    public List<TaxonBase> getTaxaByCommonName(String queryString,
1628
            Classification classification, MatchMode matchMode,
1629
            Set<NamedArea> namedAreas, Integer pageSize, Integer pageNumber) {
1630
        logger.warn("getTaxaByCommonName not yet implemented.");
1631
        return null;
1632
    }
1633

    
1634

    
1635

    
1636

    
1637
/*	private void xxx(List<SynonymRelationship> synonymRelationships, HashMap <UUID, ZoologicalName> zooHashMap, SynonymRelationshipType type, String addString){
1638

    
1639
        for (SynonymRelationship synonymRelation:synonymRelationships){
1640
            TaxonNameBase synName;
1641
            NonViralName inferredSynName;
1642
            Synonym syn = synonymRelation.getSynonym();
1643
            HibernateProxyHelper.deproxy(syn);
1644

    
1645
            synName = syn.getName();
1646
            ZoologicalName zooName = zooHashMap.get(synName.getUuid());
1647
            String synGenusName = zooName.getGenusOrUninomial();
1648

    
1649
            switch(type.getId()){
1650
            case SynonymRelationshipType.INFERRED_EPITHET_OF().getId():
1651
                inferredSynName.setSpecificEpithet(addString);
1652
                break;
1653
            case SynonymRelationshipType.INFERRED_GENUS_OF().getId():
1654
                break;
1655
            case SynonymRelationshipType.POTENTIAL_COMBINATION_OF().getId():
1656
                break;
1657
            default:
1658
            }
1659
            if (!synonymsGenus.contains(synGenusName)){
1660
                synonymsGenus.add(synGenusName);
1661
            }
1662
            inferredSynName = NonViralName.NewInstance(Rank.SPECIES());
1663
            inferredSynName.setSpecificEpithet(epithetOfTaxon);
1664
            inferredSynName.setGenusOrUninomial(synGenusName);
1665
            inferredEpithet = Synonym.NewInstance(inferredSynName, null);
1666
            taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_GENUS_OF());
1667
            inferredSynonyms.add(inferredEpithet);
1668
            inferredSynName.generateTitle();
1669
            taxonNames.add(inferredSynName.getNameCache());
1670
        }
1671

    
1672

    
1673
        if (!taxonNames.isEmpty()){
1674
        List<String> synNotInCDM = this.taxaByNameNotInDB(taxonNames);
1675
        ZoologicalName name;
1676
        if (!synNotInCDM.isEmpty()){
1677
            for (Synonym syn :inferredSynonyms){
1678
                name =zooHashMap.get(syn.getName().getUuid());
1679
                if (!synNotInCDM.contains(name.getNameCache())){
1680
                    inferredSynonyms.remove(syn);
1681
                }
1682
            }
1683
        }
1684
        }
1685
    }*/
1686

    
1687
    @Override
1688
    public int countAllRelationships() {
1689
        return countAllRelationships(null);
1690
    }
1691

    
1692

    
1693
    //FIXME add to interface or make private
1694
    public int countAllRelationships(Class<? extends RelationshipBase> clazz) {
1695
        if (clazz != null && ! TaxonRelationship.class.isAssignableFrom(clazz) && ! SynonymRelationship.class.isAssignableFrom(clazz) ){
1696
            throw new RuntimeException("Class must be assignable by a taxon or snonym relation");
1697
        }
1698
        int size = 0;
1699

    
1700
        if (clazz == null || TaxonRelationship.class.isAssignableFrom(clazz)){
1701
            String hql = " SELECT count(rel) FROM TaxonRelationship rel";
1702
            size += (Long)getSession().createQuery(hql).list().get(0);
1703
        }
1704
        if (clazz == null || SynonymRelationship.class.isAssignableFrom(clazz)){
1705
            String hql = " SELECT count(rel) FROM SynonymRelationship rel";
1706
            size += (Long)getSession().createQuery(hql).list().get(0);
1707
        }
1708
        return size;
1709
    }
1710

    
1711
    @Override
1712
    public List<String> taxaByNameNotInDB(List<String> taxonNames){
1713
        List<TaxonBase> notInDB = new ArrayList<TaxonBase>();
1714
        //get all taxa, already in db
1715
        Query query = getSession().createQuery("from TaxonNameBase t where t.nameCache IN (:taxonList)");
1716
        query.setParameterList("taxonList", taxonNames);
1717
        List<TaxonNameBase> taxaInDB = query.list();
1718
        //compare the original list with the result of the query
1719
        for (TaxonNameBase taxonName: taxaInDB){
1720
            if (taxonName.isInstanceOf(NonViralName.class)) {
1721
                NonViralName nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1722
                String nameCache = nonViralName.getNameCache();
1723
                if (taxonNames.contains(nameCache)){
1724
                    taxonNames.remove(nameCache);
1725
                }
1726
            }
1727
        }
1728

    
1729
        return taxonNames;
1730
    }
1731

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

    
1738
        return zooNames;
1739

    
1740
    }
1741

    
1742
    @Override
1743
    public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPaths) {
1744

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

    
1747
        List<TaxonNameBase> zooNames = query.list();
1748

    
1749
        TaxonNameComparator taxComp = new TaxonNameComparator();
1750
        Collections.sort(zooNames, taxComp);
1751

    
1752
        for (TaxonNameBase taxonNameBase: zooNames){
1753
            defaultBeanInitializer.initialize(taxonNameBase, propertyPaths);
1754
        }
1755

    
1756
        return zooNames;
1757
    }
1758

    
1759
    @Override
1760
    public List<TaxonNameBase> findIdenticalNamesNew(List<String> propertyPaths){
1761

    
1762
        //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)
1763
        Query query = getSession().createQuery("Select id from Reference where titleCache like 'Fauna Europaea database'");
1764
        List<String> secRefFauna = query.list();
1765
        query = getSession().createQuery("Select id from Reference where titleCache like 'ERMS'");
1766
        List<String> secRefErms = query.list();
1767
        //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");
1768
        //Get all names of fauna europaea
1769
        query = getSession().createQuery("select zn.nameCache from ZoologicalName zn, TaxonBase tb where tb.name = zn and tb.sec.id = :secRefFauna");
1770
        query.setParameter("secRefFauna", secRefFauna.get(0));
1771
        List<String> namesFauna= query.list();
1772

    
1773
        //Get all names of erms
1774

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

    
1778
        List<String> namesErms = query.list();
1779
        /*TaxonNameComparator comp = new TaxonNameComparator();
1780
        Collections.sort(namesFauna);
1781
        Collections.sort(namesErms);
1782
        */
1783
        List <String> identicalNames = new ArrayList<String>();
1784
        String predecessor = "";
1785

    
1786
        for (String nameFauna: namesFauna){
1787
            if (namesErms.contains(nameFauna)){
1788
                identicalNames.add(nameFauna);
1789
            }
1790
        }
1791

    
1792

    
1793
        query = getSession().createQuery("from ZoologicalName zn where zn.nameCache IN (:identicalNames)");
1794
        query.setParameterList("identicalNames", identicalNames);
1795
        List<TaxonNameBase> result = query.list();
1796
        TaxonNameBase temp = result.get(0);
1797

    
1798
        Iterator<OriginalSourceBase> sources = temp.getSources().iterator();
1799

    
1800
        TaxonNameComparator taxComp = new TaxonNameComparator();
1801
        Collections.sort(result, taxComp);
1802
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1803
        return result;
1804

    
1805
    }
1806

    
1807

    
1808

    
1809
    @Override
1810
    public String getPhylumName(TaxonNameBase name){
1811
        List results = new ArrayList();
1812
        try{
1813
        Query query = getSession().createSQLQuery("select getPhylum("+ name.getId()+");");
1814
        results = query.list();
1815
        }catch(Exception e){
1816
            System.err.println(name.getUuid());
1817
            return null;
1818
        }
1819
        System.err.println("phylum of "+ name.getTitleCache() );
1820
        return (String)results.get(0);
1821
    }
1822

    
1823

    
1824
    @Override
1825
    public long countTaxaByCommonName(String searchString,
1826
            Classification classification, MatchMode matchMode,
1827
            Set<NamedArea> namedAreas) {
1828
        boolean doCount = true;
1829
        Query query = prepareTaxaByCommonName(searchString, classification, matchMode, namedAreas, null, null, doCount, false);
1830
        if (query != null && !query.list().isEmpty()) {
1831
            Object o = query.uniqueResult();
1832
            if(o != null) {
1833
                return (Long)o;
1834
            }
1835
        }
1836
        return 0;
1837
    }
1838

    
1839
    @Override
1840
    public long deleteSynonymRelationships(Synonym synonym, Taxon taxon) {
1841

    
1842
        String hql = "delete SynonymRelationship sr where sr.relatedFrom = :syn ";
1843
        if (taxon != null){
1844
            hql += " and sr.relatedTo = :taxon";
1845
        }
1846
        Session session = this.getSession();
1847
        Query q = session.createQuery(hql);
1848

    
1849
        q.setParameter("syn", synonym);
1850
        if (taxon != null){
1851
            q.setParameter("taxon", taxon);
1852
        }
1853
        long result = q.executeUpdate();
1854

    
1855
        return result;
1856
    }
1857

    
1858

    
1859
    @Override
1860
    public Integer countSynonymRelationships(TaxonBase taxonBase,
1861
            SynonymRelationshipType type, Direction relatedfrom) {
1862
        AuditEvent auditEvent = getAuditEventFromContext();
1863
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1864
            Query query = null;
1865

    
1866
            if(type == null) {
1867
                query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship."+relatedfrom+" = :relatedSynonym");
1868
            } else {
1869
                query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship."+relatedfrom+" = :relatedSynonym and synonymRelationship.type = :type");
1870
                query.setParameter("type",type);
1871
            }
1872
            query.setParameter("relatedTaxon", taxonBase);
1873

    
1874
            return ((Long)query.uniqueResult()).intValue();
1875
        } else {
1876
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1877
            query.add(AuditEntity.relatedId(relatedfrom.toString()).eq(taxonBase.getId()));
1878
            query.addProjection(AuditEntity.id().count());
1879

    
1880
            if(type != null) {
1881
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1882
            }
1883

    
1884
            return ((Long)query.getSingleResult()).intValue();
1885
        }
1886
    }
1887

    
1888

    
1889
    @Override
1890
    public List<SynonymRelationship> getSynonymRelationships(TaxonBase taxonBase,
1891
            SynonymRelationshipType type, Integer pageSize, Integer pageNumber,
1892
            List<OrderHint> orderHints, List<String> propertyPaths,
1893
            Direction direction) {
1894

    
1895
        AuditEvent auditEvent = getAuditEventFromContext();
1896
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1897
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1898

    
1899
            if (direction.equals(Direction.relatedTo)){
1900
                criteria.add(Restrictions.eq("relatedTo", taxonBase));
1901
            }else{
1902
                criteria.add(Restrictions.eq("relatedFrom", taxonBase));
1903
            }
1904
            if(type != null) {
1905
                criteria.add(Restrictions.eq("type", type));
1906
            }
1907

    
1908
            addOrder(criteria,orderHints);
1909

    
1910
            if(pageSize != null) {
1911
                criteria.setMaxResults(pageSize);
1912
                if(pageNumber != null) {
1913
                    criteria.setFirstResult(pageNumber * pageSize);
1914
                } else {
1915
                    criteria.setFirstResult(0);
1916
                }
1917
            }
1918

    
1919
            List<SynonymRelationship> result = criteria.list();
1920
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1921

    
1922
            return result;
1923
        } else {
1924
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1925

    
1926
            if (direction.equals(Direction.relatedTo)){
1927
                query.add(AuditEntity.relatedId("relatedTo").eq(taxonBase.getId()));
1928
            }else{
1929
                query.add(AuditEntity.relatedId("relatedFrom").eq(taxonBase.getId()));
1930
            }
1931

    
1932
            if(type != null) {
1933
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1934
            }
1935

    
1936
            if(pageSize != null) {
1937
                query.setMaxResults(pageSize);
1938
                if(pageNumber != null) {
1939
                    query.setFirstResult(pageNumber * pageSize);
1940
                } else {
1941
                    query.setFirstResult(0);
1942
                }
1943
            }
1944

    
1945
            List<SynonymRelationship> result = query.getResultList();
1946
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1947

    
1948
            // Ugly, but for now, there is no way to sort on a related entity property in Envers,
1949
            // and we can't live without this functionality in CATE as it screws up the whole
1950
            // taxon tree thing
1951
            if(orderHints != null && !orderHints.isEmpty()) {
1952
                SortedSet<SynonymRelationship> sortedList = new TreeSet<SynonymRelationship>(new SynonymRelationshipFromTaxonComparator());
1953
                sortedList.addAll(result);
1954
                return new ArrayList<SynonymRelationship>(sortedList);
1955
            }
1956

    
1957
            return result;
1958
        }
1959
    }
1960

    
1961

    
1962
    @Override
1963
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon(Integer limit, String pattern) {
1964
        return getUuidAndTitleCache(limit, pattern, true);
1965
    }
1966

    
1967

    
1968
  /*  public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCache(Integer limit, String pattern, boolean isTaxon) {
1969
        String className;
1970
        if (isTaxon){
1971
            className = Taxon.class.getSimpleName();
1972
        } else{
1973
            className = Synonym.class.getSimpleName();
1974
        }
1975
        String queryString;
1976

    
1977
        if(pattern == null){
1978
            queryString = String.format("select uuid, id, titleCache from %s where DTYPE = '%s' ", type.getSimpleName(), className );
1979
       } else{
1980
           queryString = String.format("select uuid, id, titleCache from %s where DTYPE = '%s' and titleCache like :pattern", type.getSimpleName(), className);
1981
       }
1982
        Query query = getSession().createQuery(queryString);
1983
        if (pattern != null){
1984
          pattern = pattern + "%";
1985
            query.setParameter("pattern", pattern);
1986
        }
1987
        if (limit  != null){
1988
            query.setMaxResults(limit);
1989
        }
1990

    
1991
        List<UuidAndTitleCache<TaxonBase>> result = getUuidAndTitleCache(query);
1992

    
1993
        return result;
1994
    }
1995
    @Override
1996
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym(Integer limit, String pattern){
1997

    
1998
        return getUuidAndTitleCache(limit, pattern, false);
1999
    }
2000
*/
2001
    @Override
2002
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {
2003
        String queryString = String.format("select uuid, id, titleCache from %s where DTYPE = '%s'", type.getSimpleName(), Taxon.class.getSimpleName());
2004
        Query query = getSession().createQuery(queryString);
2005

    
2006
        List<UuidAndTitleCache<TaxonBase>> result = getUuidAndTitleCache(query);
2007

    
2008
        return result;
2009
    }
2010

    
2011
    @Override
2012
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {
2013
        String queryString = String.format("select uuid, id, titleCache from %s where DTYPE = '%s'", type.getSimpleName(), Synonym.class.getSimpleName());
2014
        Query query = getSession().createQuery(queryString);
2015

    
2016
        List<UuidAndTitleCache<TaxonBase>> result = getUuidAndTitleCache(query);
2017

    
2018
        return result;
2019
    }
2020
    private String[] createHQLString(boolean doTaxa, boolean doSynonyms, boolean doIncludeMisappliedNames, Classification classification,  Set<NamedArea> areasExpanded, MatchMode matchMode, String searchField){
2021

    
2022
           boolean doAreaRestriction = areasExpanded.size() > 0;
2023
           String 	doAreaRestrictionSubSelect = "select %s.id from" +
2024
                " Distribution e" +
2025
                " join e.inDescription d" +
2026
                " join d.taxon t" +
2027
                (classification != null ? " join t.taxonNodes as tn " : " ");
2028

    
2029
           String 	doAreaRestrictionMisappliedNameSubSelect = "select %s.id from" +
2030
            " Distribution e" +
2031
            " join e.inDescription d" +
2032
            " join d.taxon t";
2033

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

    
2037
           String doTaxonNameJoin =   " join t.name n ";
2038

    
2039
           String doSynonymNameJoin =  	" join t.synonymRelations sr join sr.relatedFrom s join s.name sn";
2040

    
2041
           String doMisappliedNamesJoin = " left join t.relationsFromThisTaxon as rft" +
2042
                " left join rft.relatedTo as rt" +
2043
                (classification != null ? " left join rt.taxonNodes as tn2" : " ") +
2044
                " left join rt.name as n2" +
2045
                " left join rft.type as rtype";
2046

    
2047
           String doClassificationWhere = " tn.classification = :classification";
2048
           String doClassificationForMisappliedNamesWhere = " tn2 .classification = :classification";
2049

    
2050
           String doAreaRestrictionWhere =  " e.area.uuid in (:namedAreasUuids)";
2051

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

    
2054
           String doRelationshipTypeComparison = " rtype = :rType ";
2055

    
2056
        String taxonSubselect = null;
2057
        String synonymSubselect = null;
2058
        String misappliedSelect = null;
2059

    
2060
        if(classification != null ){
2061
            if (!doIncludeMisappliedNames){
2062
                if(doAreaRestriction){
2063
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2064
                    " WHERE " + doAreaRestrictionWhere +
2065
                    " AND " + doClassificationWhere +
2066
                    " AND " + String.format(doSearchFieldWhere, "n");
2067
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2068
                    " WHERE " + doAreaRestrictionWhere +
2069
                    " AND " + doClassificationWhere +
2070
                    " AND " + String.format(doSearchFieldWhere, "sn");
2071
                } else {
2072
                    taxonSubselect = String.format(doTaxonSubSelect, "t" )+ doTaxonNameJoin +
2073
                    " WHERE " + doClassificationWhere +
2074
                    " AND " + String.format(doSearchFieldWhere, "n");
2075
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
2076
                    " WHERE " + doClassificationWhere +
2077
                    " AND " + String.format(doSearchFieldWhere, "sn");
2078
                }
2079
            }else{ //misappliedNames included
2080
                if(doAreaRestriction){
2081
                    misappliedSelect = String.format(doAreaRestrictionMisappliedNameSubSelect, "t") + doTaxonNameJoin + doMisappliedNamesJoin +
2082
                    " WHERE " + doAreaRestrictionWhere +
2083
                    " AND " + String.format(doSearchFieldWhere, "n") +
2084
                    " AND " + doClassificationForMisappliedNamesWhere +
2085
                    " AND " + doRelationshipTypeComparison;
2086

    
2087
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2088
                    " WHERE " + doAreaRestrictionWhere +
2089
                    " AND "+ String.format(doSearchFieldWhere, "n") + " AND "+ doClassificationWhere;
2090

    
2091
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2092
                    " WHERE " + doAreaRestrictionWhere +
2093
                    " AND " + doClassificationWhere + " AND " +  String.format(doSearchFieldWhere, "sn");;
2094

    
2095
                } else {
2096
                    misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin +
2097
                    " WHERE " + String.format(doSearchFieldWhere, "n") +
2098
                    " AND " + doClassificationForMisappliedNamesWhere +
2099
                    " AND " + doRelationshipTypeComparison;
2100

    
2101
                    taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin +
2102
                    " WHERE " +  String.format(doSearchFieldWhere, "n") +
2103
                    " AND "+ doClassificationWhere;
2104

    
2105
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
2106
                    " WHERE " + doClassificationWhere +
2107
                    " AND " +  String.format(doSearchFieldWhere, "sn");
2108

    
2109
                }
2110
            }
2111
        } else {
2112
            if(doAreaRestriction){
2113
                misappliedSelect = String.format(doAreaRestrictionMisappliedNameSubSelect, "t") + doTaxonNameJoin + doMisappliedNamesJoin +
2114
                " WHERE " + doAreaRestrictionWhere +
2115
                " AND " + String.format(doSearchFieldWhere, "n")+
2116
                " AND " + doRelationshipTypeComparison;
2117

    
2118
                taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2119
                " WHERE " + doAreaRestrictionWhere +
2120
                " AND " + String.format(doSearchFieldWhere, "n");
2121

    
2122
                synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2123
                " WHERE " +   doAreaRestrictionWhere +
2124
                " AND " +  String.format(doSearchFieldWhere, "sn");
2125

    
2126

    
2127
            } else {
2128
                misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin + " WHERE " +  String.format(doSearchFieldWhere, "n") + " AND " + doRelationshipTypeComparison;
2129
                taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "n");
2130
                synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "sn");
2131

    
2132
            }
2133
        }
2134
        String[] result = {misappliedSelect, taxonSubselect, synonymSubselect};
2135

    
2136
        return result;
2137
    }
2138

    
2139
	@Override
2140
	public List<UuidAndTitleCache<IdentifiableEntity>> getTaxaByCommonNameForEditor(
2141
			String titleSearchStringSqlized, Classification classification,
2142
			MatchMode matchMode, Set namedAreas) {
2143
	    List<Object> resultArray = new ArrayList<Object>();
2144
		Query query = prepareTaxaByCommonName(titleSearchStringSqlized, classification, matchMode, namedAreas, null, null, false, true);
2145
        if (query != null){
2146
            resultArray = query.list();
2147
            List<UuidAndTitleCache<IdentifiableEntity>> returnResult = new ArrayList<UuidAndTitleCache<IdentifiableEntity>>() ;
2148
            Object[] result;
2149
            for(int i = 0; i<resultArray.size();i++){
2150
            	result = (Object[]) resultArray.get(i);
2151
            	returnResult.add(new UuidAndTitleCache(Taxon.class, (UUID) result[0],(Integer)result[1], (String)result[2], new Boolean(result[4].toString())));
2152
            }
2153
            return returnResult;
2154
        }
2155

    
2156
		return null;
2157
	}
2158

    
2159

    
2160
	/**
2161
	 * @param
2162
	 * @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)
2163
	 */
2164
	@Override
2165
	public <S extends TaxonBase> int countByIdentifier(Class<S> clazz,
2166
			String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter, MatchMode matchmode) {
2167
		if (subtreeFilter == null){
2168
			return countByIdentifier(clazz, identifier, identifierType, matchmode);
2169
		}
2170

    
2171
		Class<?> clazzParam = clazz == null ? type : clazz;
2172
		checkNotInPriorView("IdentifiableDaoBase.countByIdentifier(T clazz, String identifier, DefinedTerm identifierType, TaxonNode subMatchMode matchmode)");
2173

    
2174
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
2175
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
2176

    
2177
		getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
2178
		String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
2179
		String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
2180
		String synTreeJoin = isSynonym ? " LEFT JOIN c.synonymRelations sr LEFT  JOIN sr.relatedTo as acc LEFT JOIN acc.taxonNodes synTn  " : "";
2181
		String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
2182
		String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
2183

    
2184
		String queryString = "SELECT count(*)  FROM %s as c " +
2185
                " INNER JOIN c.identifiers as ids " +
2186
                accTreeJoin +
2187
                synTreeJoin +
2188
                " WHERE (1=1) " +
2189
                	"  AND ( " + accWhere + " OR " + synWhere + ")";
2190
		queryString = String.format(queryString, clazzParam.getSimpleName());
2191

    
2192
		if (identifier != null){
2193
			if (matchmode == null || matchmode == MatchMode.EXACT){
2194
				queryString += " AND ids.identifier = '"  + identifier + "'";
2195
			}else {
2196
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
2197
			}
2198
		}
2199
		if (identifierType != null){
2200
        	queryString += " AND ids.type = :type";
2201
        }
2202

    
2203
		Query query = getSession().createQuery(queryString);
2204
        if (identifierType != null){
2205
        	query.setEntity("type", identifierType);
2206
        }
2207

    
2208
		Long c = (Long)query.uniqueResult();
2209
        return c.intValue();
2210
	}
2211

    
2212
	@Override
2213
	public <S extends TaxonBase> List<Object[]> findByIdentifier(
2214
			Class<S> clazz, String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter,
2215
			MatchMode matchmode, boolean includeEntity,
2216
			Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
2217

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

    
2221
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
2222
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
2223
		getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
2224
		String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
2225
		String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
2226
		String synTreeJoin = isSynonym ? " LEFT JOIN c.synonymRelations sr LEFT  JOIN sr.relatedTo as acc LEFT JOIN acc.taxonNodes synTn  " : "";
2227
		String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
2228
		String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
2229

    
2230
		String queryString = "SELECT ids.type, ids.identifier, %s " +
2231
				" FROM %s as c " +
2232
                " INNER JOIN c.identifiers as ids " +
2233
                accTreeJoin +
2234
				synTreeJoin +
2235
                " WHERE (1=1) " +
2236
                	" AND ( " + accWhere + " OR " + synWhere + ")";
2237
		queryString = String.format(queryString, (includeEntity ? "c":"c.uuid, c.titleCache") , clazzParam.getSimpleName());
2238

    
2239
		//Matchmode and identifier
2240
		if (identifier != null){
2241
			if (matchmode == null || matchmode == MatchMode.EXACT){
2242
				queryString += " AND ids.identifier = '"  + identifier + "'";
2243
			}else {
2244
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
2245
			}
2246
		}
2247
        if (identifierType != null){
2248
        	queryString += " AND ids.type = :type";
2249
        }
2250
        //order
2251
        queryString +=" ORDER BY ids.type.uuid, ids.identifier, c.uuid ";
2252

    
2253
		Query query = getSession().createQuery(queryString);
2254

    
2255
		//parameters
2256
		if (identifierType != null){
2257
        	query.setEntity("type", identifierType);
2258
        }
2259

    
2260
        //paging
2261
        setPagingParameter(query, pageSize, pageNumber);
2262

    
2263
        List<Object[]> results = query.list();
2264
        //initialize
2265
        if (includeEntity){
2266
        	List<S> entities = new ArrayList<S>();
2267
        	for (Object[] result : results){
2268
        		entities.add((S)result[2]);
2269
        	}
2270
        	defaultBeanInitializer.initializeAll(entities, propertyPaths);
2271
        }
2272
        return results;
2273
	}
2274

    
2275
    /* (non-Javadoc)
2276
     * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(eu.etaxonomy.cdm.model.taxon.Classification)
2277
     */
2278
    @Override
2279
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
2280
            Classification classification) {
2281

    
2282
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification,null);
2283
    }
2284
}
(3-3/4)