Project

General

Profile

Download (94.8 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
        @SuppressWarnings("unchecked")
142
        List<Taxon> taxa = crit.list();
143
        for(Taxon taxon : taxa){
144

    
145

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

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

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

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

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

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

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

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

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

    
200
        return criteria.list();
201
    }
202

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

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

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

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

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

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

    
229
        boolean doCount = false;
230

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

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

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

    
243
        return new ArrayList<TaxonBase>();
244

    
245
    }
246

    
247

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

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

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

    
272

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

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

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

    
297

    
298

    
299
        }
300

    
301
        return resultObjects;
302

    
303
    }
304

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

    
318
    }
319

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

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

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

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

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

    
379

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

    
385

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

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

    
401

    
402

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

    
416
                    } else {
417

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

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

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

    
479

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

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

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

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

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

    
567
                    } else {
568

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

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

    
585
                }
586

    
587
            }*/
588

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

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

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

    
605
                }
606

    
607

    
608
            }
609

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

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

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

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

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

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

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

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

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

    
726
            }
727

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

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

    
741

    
742
            if (hql == "") {
743
                return null;
744
            }
745
            if(!doCount){
746
                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";
747
            }
748

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

    
752

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

    
788
            return query;
789
    }
790

    
791

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

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

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

    
818
        return query;
819
    }
820

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

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

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

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

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

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

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

    
864

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

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

    
891

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

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

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

    
906
        return criteria.list();
907
    }
908

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

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

    
918
        return criteria.list();
919
    }
920

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

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

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

    
933
            if (taxRelSize > start){
934

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

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

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

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

    
976
        taxonBase.removeSources();
977

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

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

    
992
       return super.delete(taxonBase);
993

    
994
    }
995

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

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

    
1008
    }
1009

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

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

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

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

    
1039
        return null;
1040
    }
1041

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

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

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

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

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

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

    
1076

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1198
        }
1199

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

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

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

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

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

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

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

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

    
1244
        }
1245

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

    
1252
        if(authorship == null) {
1253
            criteria.add(Restrictions.eq("name.authorshipCache", ""));
1254
        } else if(!authorship.equals("*")) {
1255
            criteria.add(Restrictions.eq("name.authorshipCache", authorship));
1256
        }
1257

    
1258
        if(rank != null) {
1259
            criteria.add(Restrictions.eq("name.rank", rank));
1260
        }
1261

    
1262
        if(pageSize != null) {
1263
            criteria.setMaxResults(pageSize);
1264
            if(pageNumber != null) {
1265
                criteria.setFirstResult(pageNumber * pageSize);
1266
            } else {
1267
                criteria.setFirstResult(0);
1268
            }
1269
        }
1270

    
1271
        return criteria.list();
1272
    }
1273

    
1274
    @Override
1275
    public List<TaxonRelationship> getTaxonRelationships(Taxon taxon, TaxonRelationshipType type,
1276
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {
1277

    
1278
        AuditEvent auditEvent = getAuditEventFromContext();
1279
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1280

    
1281
            Criteria criteria = getSession().createCriteria(TaxonRelationship.class);
1282

    
1283
            if(direction != null) {
1284
                criteria.add(Restrictions.eq(direction.name(), taxon));
1285
            } else {
1286
                criteria.add(Restrictions.or(
1287
                        Restrictions.eq(Direction.relatedFrom.name(), taxon),
1288
                        Restrictions.eq(Direction.relatedTo.name(), taxon))
1289
                    );
1290
            }
1291

    
1292
            if(type != null) {
1293
                criteria.add(Restrictions.eq("type", type));
1294
            }
1295

    
1296
            addOrder(criteria,orderHints);
1297

    
1298
            if(pageSize != null) {
1299
                criteria.setMaxResults(pageSize);
1300
                if(pageNumber != null) {
1301
                    criteria.setFirstResult(pageNumber * pageSize);
1302
                } else {
1303
                    criteria.setFirstResult(0);
1304
                }
1305
            }
1306

    
1307
            List<TaxonRelationship> result = criteria.list();
1308
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1309

    
1310
            return result;
1311
        } else {
1312
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1313
            query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));
1314

    
1315
            if(type != null) {
1316
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1317
            }
1318

    
1319
            if(pageSize != null) {
1320
                query.setMaxResults(pageSize);
1321
                if(pageNumber != null) {
1322
                    query.setFirstResult(pageNumber * pageSize);
1323
                } else {
1324
                    query.setFirstResult(0);
1325
                }
1326
            }
1327

    
1328
            List<TaxonRelationship> result = query.getResultList();
1329
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1330

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

    
1340
            return result;
1341
        }
1342
    }
1343

    
1344
    class TaxonRelationshipFromTaxonComparator implements Comparator<TaxonRelationship> {
1345

    
1346
        @Override
1347
        public int compare(TaxonRelationship o1, TaxonRelationship o2) {
1348
            return o1.getFromTaxon().getTitleCache().compareTo(o2.getFromTaxon().getTitleCache());
1349
        }
1350

    
1351
    }
1352

    
1353
    class SynonymRelationshipFromTaxonComparator implements Comparator<SynonymRelationship> {
1354

    
1355
        @Override
1356
        public int compare(SynonymRelationship o1, SynonymRelationship o2) {
1357
            return o1.getSynonym().getTitleCache().compareTo(o2.getSynonym().getTitleCache());
1358
        }
1359

    
1360
    }
1361

    
1362
    @Override
1363
    public List<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
1364
        AuditEvent auditEvent = getAuditEventFromContext();
1365
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1366
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1367

    
1368
            criteria.add(Restrictions.eq("relatedTo", taxon));
1369
            if(type != null) {
1370
                criteria.add(Restrictions.eq("type", type));
1371
            }
1372

    
1373
            addOrder(criteria,orderHints);
1374

    
1375
            if(pageSize != null) {
1376
                criteria.setMaxResults(pageSize);
1377
                if(pageNumber != null) {
1378
                    criteria.setFirstResult(pageNumber * pageSize);
1379
                } else {
1380
                    criteria.setFirstResult(0);
1381
                }
1382
            }
1383

    
1384
            List<SynonymRelationship> result = criteria.list();
1385
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1386

    
1387
            return result;
1388
        } else {
1389
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());
1390
            query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));
1391

    
1392
            if(type != null) {
1393
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1394
            }
1395

    
1396
            if(pageSize != null) {
1397
                query.setMaxResults(pageSize);
1398
                if(pageNumber != null) {
1399
                    query.setFirstResult(pageNumber * pageSize);
1400
                } else {
1401
                    query.setFirstResult(0);
1402
                }
1403
            }
1404

    
1405
            List<SynonymRelationship> result = query.getResultList();
1406
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1407

    
1408
            return result;
1409
        }
1410
    }
1411

    
1412
    @Override
1413
    public List<SynonymRelationship> getSynonyms(Synonym synonym, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
1414
        AuditEvent auditEvent = getAuditEventFromContext();
1415
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1416
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1417

    
1418
            criteria.add(Restrictions.eq("relatedFrom", synonym));
1419
            if(type != null) {
1420
                criteria.add(Restrictions.eq("type", type));
1421
            }
1422

    
1423
            addOrder(criteria,orderHints);
1424

    
1425
            if(pageSize != null) {
1426
                criteria.setMaxResults(pageSize);
1427
                if(pageNumber != null) {
1428
                    criteria.setFirstResult(pageNumber * pageSize);
1429
                } else {
1430
                    criteria.setFirstResult(0);
1431
                }
1432
            }
1433

    
1434
            List<SynonymRelationship> result = criteria.list();
1435
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1436

    
1437
            return result;
1438
        } else {
1439
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());
1440
            query.add(AuditEntity.relatedId("relatedFrom").eq(synonym.getId()));
1441

    
1442
            if(type != null) {
1443
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1444
            }
1445

    
1446
            if(pageSize != null) {
1447
                query.setMaxResults(pageSize);
1448
                if(pageNumber != null) {
1449
                    query.setFirstResult(pageNumber * pageSize);
1450
                } else {
1451
                    query.setFirstResult(0);
1452
                }
1453
            }
1454

    
1455
            List<SynonymRelationship> result = query.getResultList();
1456
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1457

    
1458
            return result;
1459
        }
1460
    }
1461

    
1462
    @Override
1463
    public void rebuildIndex() {
1464
        FullTextSession fullTextSession = Search.getFullTextSession(getSession());
1465

    
1466
        for(TaxonBase taxonBase : list(null,null)) { // re-index all taxon base
1467
            Hibernate.initialize(taxonBase.getName());
1468
            fullTextSession.index(taxonBase);
1469
        }
1470
        fullTextSession.flushToIndexes();
1471
    }
1472

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

    
1495
    @Override
1496
    public List<Taxon> listAcceptedTaxaFor(Synonym synonym, Classification classificationFilter, Integer pageSize, Integer pageNumber,
1497
            List<OrderHint> orderHints, List<String> propertyPaths){
1498

    
1499
        String hql = prepareListAcceptedTaxaFor(classificationFilter, orderHints, false);
1500

    
1501
        Query query = getSession().createQuery(hql);
1502

    
1503
        query.setParameter("synonym", synonym);
1504

    
1505
        if(classificationFilter != null){
1506
            query.setParameter("classificationFilter", classificationFilter);
1507
        }
1508

    
1509

    
1510
        if(pageSize != null) {
1511
            query.setMaxResults(pageSize);
1512
            if(pageNumber != null) {
1513
                query.setFirstResult(pageNumber * pageSize);
1514
            }
1515
        }
1516

    
1517
        @SuppressWarnings("unchecked")
1518
        List<Taxon> result = query.list();
1519

    
1520
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1521

    
1522
        return result;
1523

    
1524
    }
1525

    
1526
    @Override
1527
    public long countAcceptedTaxaFor(Synonym synonym, Classification classificationFilter){
1528

    
1529
        String hql = prepareListAcceptedTaxaFor(classificationFilter, null, true);
1530

    
1531
        Query query = getSession().createQuery(hql);
1532

    
1533
        query.setParameter("synonym", synonym);
1534

    
1535
        if(classificationFilter != null){
1536
            query.setParameter("classificationFilter", classificationFilter);
1537
        }
1538

    
1539
        Long count = Long.parseLong(query.uniqueResult().toString());
1540

    
1541
        return count;
1542

    
1543
    }
1544

    
1545

    
1546
    /**
1547
     * @param classificationFilter
1548
     * @param orderHints
1549
     * @return
1550
     */
1551
    private String prepareListAcceptedTaxaFor(Classification classificationFilter, List<OrderHint> orderHints, boolean doCount) {
1552

    
1553
        String hql;
1554
        String hqlSelect = "select " + (doCount? "count(taxon)" : "taxon") + " from Taxon as taxon left join taxon.synonymRelations as synRel ";
1555
        String hqlWhere = " where synRel.relatedFrom = :synonym";
1556

    
1557
        if(classificationFilter != null){
1558
            hqlSelect += " left join taxon.taxonNodes AS taxonNode";
1559
            hqlWhere += " and taxonNode.classification = :classificationFilter";
1560
        }
1561
        hql = hqlSelect + hqlWhere + orderByClause(orderHints, "taxon");
1562
        return hql;
1563
    }
1564
    @Override
1565
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, Integer limit, String pattern) {
1566
        int classificationId = classification.getId();
1567
        // StringBuffer excludeUuids = new StringBuffer();
1568

    
1569
         String queryString = "SELECT nodes.uuid, nodes.id, taxon.titleCache FROM TaxonNode AS nodes JOIN nodes.taxon as taxon WHERE nodes.classification.id = " + classificationId ;
1570

    
1571
         if (pattern != null){
1572
             pattern = pattern.replace("*", "%");
1573
             queryString = queryString + " AND taxon.titleCache like (:pattern)" ;
1574
         }
1575

    
1576
         Query query = getSession().createQuery(queryString);
1577

    
1578

    
1579
         if (limit != null){
1580
             query.setMaxResults(limit);
1581
         }
1582

    
1583
         if (pattern != null){
1584
             query.setParameter("pattern", pattern);
1585
         }
1586
         @SuppressWarnings("unchecked")
1587
         List<Object[]> result = query.list();
1588

    
1589
         if(result.size() == 0){
1590
             return null;
1591
         }else{
1592
             List<UuidAndTitleCache<TaxonNode>> list = new ArrayList<UuidAndTitleCache<TaxonNode>>(result.size());
1593

    
1594
             for (Object object : result){
1595

    
1596
                 Object[] objectArray = (Object[]) object;
1597

    
1598
                 UUID uuid = (UUID)objectArray[0];
1599
                 Integer id = (Integer) objectArray[1];
1600
                 String titleCache = (String) objectArray[2];
1601

    
1602
                 list.add(new UuidAndTitleCache<TaxonNode>(TaxonNode.class, uuid, id, titleCache));
1603
             }
1604

    
1605
             return list;
1606
         }
1607
    }
1608

    
1609

    
1610
    @Override
1611
    public TaxonBase find(LSID lsid) {
1612
        TaxonBase<?> taxonBase = super.find(lsid);
1613
        if(taxonBase != null) {
1614
            List<String> propertyPaths = new ArrayList<String>();
1615
            propertyPaths.add("createdBy");
1616
            propertyPaths.add("updatedBy");
1617
            propertyPaths.add("name");
1618
            propertyPaths.add("sec");
1619
            propertyPaths.add("relationsToThisTaxon");
1620
            propertyPaths.add("relationsToThisTaxon.fromTaxon");
1621
            propertyPaths.add("relationsToThisTaxon.toTaxon");
1622
            propertyPaths.add("relationsFromThisTaxon");
1623
            propertyPaths.add("relationsFromThisTaxon.toTaxon");
1624
            propertyPaths.add("relationsToThisTaxon.type");
1625
            propertyPaths.add("synonymRelations");
1626
            propertyPaths.add("synonymRelations.synonym");
1627
            propertyPaths.add("synonymRelations.type");
1628
            propertyPaths.add("descriptions");
1629

    
1630
            defaultBeanInitializer.initialize(taxonBase, propertyPaths);
1631
        }
1632
        return taxonBase;
1633
    }
1634

    
1635
    public List<TaxonBase> getTaxaByCommonName(String queryString,
1636
            Classification classification, MatchMode matchMode,
1637
            Set<NamedArea> namedAreas, Integer pageSize, Integer pageNumber) {
1638
        logger.warn("getTaxaByCommonName not yet implemented.");
1639
        return null;
1640
    }
1641

    
1642

    
1643

    
1644

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

    
1647
        for (SynonymRelationship synonymRelation:synonymRelationships){
1648
            TaxonNameBase synName;
1649
            NonViralName inferredSynName;
1650
            Synonym syn = synonymRelation.getSynonym();
1651
            HibernateProxyHelper.deproxy(syn);
1652

    
1653
            synName = syn.getName();
1654
            ZoologicalName zooName = zooHashMap.get(synName.getUuid());
1655
            String synGenusName = zooName.getGenusOrUninomial();
1656

    
1657
            switch(type.getId()){
1658
            case SynonymRelationshipType.INFERRED_EPITHET_OF().getId():
1659
                inferredSynName.setSpecificEpithet(addString);
1660
                break;
1661
            case SynonymRelationshipType.INFERRED_GENUS_OF().getId():
1662
                break;
1663
            case SynonymRelationshipType.POTENTIAL_COMBINATION_OF().getId():
1664
                break;
1665
            default:
1666
            }
1667
            if (!synonymsGenus.contains(synGenusName)){
1668
                synonymsGenus.add(synGenusName);
1669
            }
1670
            inferredSynName = NonViralName.NewInstance(Rank.SPECIES());
1671
            inferredSynName.setSpecificEpithet(epithetOfTaxon);
1672
            inferredSynName.setGenusOrUninomial(synGenusName);
1673
            inferredEpithet = Synonym.NewInstance(inferredSynName, null);
1674
            taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_GENUS_OF());
1675
            inferredSynonyms.add(inferredEpithet);
1676
            inferredSynName.generateTitle();
1677
            taxonNames.add(inferredSynName.getNameCache());
1678
        }
1679

    
1680

    
1681
        if (!taxonNames.isEmpty()){
1682
        List<String> synNotInCDM = this.taxaByNameNotInDB(taxonNames);
1683
        ZoologicalName name;
1684
        if (!synNotInCDM.isEmpty()){
1685
            for (Synonym syn :inferredSynonyms){
1686
                name =zooHashMap.get(syn.getName().getUuid());
1687
                if (!synNotInCDM.contains(name.getNameCache())){
1688
                    inferredSynonyms.remove(syn);
1689
                }
1690
            }
1691
        }
1692
        }
1693
    }*/
1694

    
1695
    @Override
1696
    public int countAllRelationships() {
1697
        return countAllRelationships(null);
1698
    }
1699

    
1700

    
1701
    //FIXME add to interface or make private
1702
    public int countAllRelationships(Class<? extends RelationshipBase> clazz) {
1703
        if (clazz != null && ! TaxonRelationship.class.isAssignableFrom(clazz) && ! SynonymRelationship.class.isAssignableFrom(clazz) ){
1704
            throw new RuntimeException("Class must be assignable by a taxon or snonym relation");
1705
        }
1706
        int size = 0;
1707

    
1708
        if (clazz == null || TaxonRelationship.class.isAssignableFrom(clazz)){
1709
            String hql = " SELECT count(rel) FROM TaxonRelationship rel";
1710
            size += (Long)getSession().createQuery(hql).list().get(0);
1711
        }
1712
        if (clazz == null || SynonymRelationship.class.isAssignableFrom(clazz)){
1713
            String hql = " SELECT count(rel) FROM SynonymRelationship rel";
1714
            size += (Long)getSession().createQuery(hql).list().get(0);
1715
        }
1716
        return size;
1717
    }
1718

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

    
1737
        return taxonNames;
1738
    }
1739

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

    
1746
        return zooNames;
1747

    
1748
    }
1749

    
1750
    @Override
1751
    public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPaths) {
1752

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

    
1755
        List<TaxonNameBase> zooNames = query.list();
1756

    
1757
        TaxonNameComparator taxComp = new TaxonNameComparator();
1758
        Collections.sort(zooNames, taxComp);
1759

    
1760
        for (TaxonNameBase taxonNameBase: zooNames){
1761
            defaultBeanInitializer.initialize(taxonNameBase, propertyPaths);
1762
        }
1763

    
1764
        return zooNames;
1765
    }
1766

    
1767
    @Override
1768
    public List<TaxonNameBase> findIdenticalNamesNew(List<String> propertyPaths){
1769

    
1770
        //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)
1771
        Query query = getSession().createQuery("Select id from Reference where titleCache like 'Fauna Europaea database'");
1772
        List<String> secRefFauna = query.list();
1773
        query = getSession().createQuery("Select id from Reference where titleCache like 'ERMS'");
1774
        List<String> secRefErms = query.list();
1775
        //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");
1776
        //Get all names of fauna europaea
1777
        query = getSession().createQuery("select zn.nameCache from ZoologicalName zn, TaxonBase tb where tb.name = zn and tb.sec.id = :secRefFauna");
1778
        query.setParameter("secRefFauna", secRefFauna.get(0));
1779
        List<String> namesFauna= query.list();
1780

    
1781
        //Get all names of erms
1782

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

    
1786
        List<String> namesErms = query.list();
1787
        /*TaxonNameComparator comp = new TaxonNameComparator();
1788
        Collections.sort(namesFauna);
1789
        Collections.sort(namesErms);
1790
        */
1791
        List <String> identicalNames = new ArrayList<String>();
1792
        String predecessor = "";
1793

    
1794
        for (String nameFauna: namesFauna){
1795
            if (namesErms.contains(nameFauna)){
1796
                identicalNames.add(nameFauna);
1797
            }
1798
        }
1799

    
1800

    
1801
        query = getSession().createQuery("from ZoologicalName zn where zn.nameCache IN (:identicalNames)");
1802
        query.setParameterList("identicalNames", identicalNames);
1803
        List<TaxonNameBase> result = query.list();
1804
        TaxonNameBase temp = result.get(0);
1805

    
1806
        Iterator<OriginalSourceBase> sources = temp.getSources().iterator();
1807

    
1808
        TaxonNameComparator taxComp = new TaxonNameComparator();
1809
        Collections.sort(result, taxComp);
1810
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1811
        return result;
1812

    
1813
    }
1814

    
1815

    
1816

    
1817
    @Override
1818
    public String getPhylumName(TaxonNameBase name){
1819
        List results = new ArrayList();
1820
        try{
1821
        Query query = getSession().createSQLQuery("select getPhylum("+ name.getId()+");");
1822
        results = query.list();
1823
        }catch(Exception e){
1824
            System.err.println(name.getUuid());
1825
            return null;
1826
        }
1827
        System.err.println("phylum of "+ name.getTitleCache() );
1828
        return (String)results.get(0);
1829
    }
1830

    
1831

    
1832
    @Override
1833
    public long countTaxaByCommonName(String searchString,
1834
            Classification classification, MatchMode matchMode,
1835
            Set<NamedArea> namedAreas) {
1836
        boolean doCount = true;
1837
        Query query = prepareTaxaByCommonName(searchString, classification, matchMode, namedAreas, null, null, doCount, false);
1838
        if (query != null && !query.list().isEmpty()) {
1839
            Object o = query.uniqueResult();
1840
            if(o != null) {
1841
                return (Long)o;
1842
            }
1843
        }
1844
        return 0;
1845
    }
1846

    
1847
    @Override
1848
    public long deleteSynonymRelationships(Synonym synonym, Taxon taxon) {
1849

    
1850
        String hql = "delete SynonymRelationship sr where sr.relatedFrom = :syn ";
1851
        if (taxon != null){
1852
            hql += " and sr.relatedTo = :taxon";
1853
        }
1854
        Session session = this.getSession();
1855
        Query q = session.createQuery(hql);
1856

    
1857
        q.setParameter("syn", synonym);
1858
        if (taxon != null){
1859
            q.setParameter("taxon", taxon);
1860
        }
1861
        long result = q.executeUpdate();
1862

    
1863
        return result;
1864
    }
1865

    
1866

    
1867
    @Override
1868
    public Integer countSynonymRelationships(TaxonBase taxonBase,
1869
            SynonymRelationshipType type, Direction relatedfrom) {
1870
        AuditEvent auditEvent = getAuditEventFromContext();
1871
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1872
            Query query = null;
1873

    
1874
            if(type == null) {
1875
                query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship."+relatedfrom+" = :relatedSynonym");
1876
            } else {
1877
                query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship."+relatedfrom+" = :relatedSynonym and synonymRelationship.type = :type");
1878
                query.setParameter("type",type);
1879
            }
1880
            query.setParameter("relatedTaxon", taxonBase);
1881

    
1882
            return ((Long)query.uniqueResult()).intValue();
1883
        } else {
1884
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1885
            query.add(AuditEntity.relatedId(relatedfrom.toString()).eq(taxonBase.getId()));
1886
            query.addProjection(AuditEntity.id().count());
1887

    
1888
            if(type != null) {
1889
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1890
            }
1891

    
1892
            return ((Long)query.getSingleResult()).intValue();
1893
        }
1894
    }
1895

    
1896

    
1897
    @Override
1898
    public List<SynonymRelationship> getSynonymRelationships(TaxonBase taxonBase,
1899
            SynonymRelationshipType type, Integer pageSize, Integer pageNumber,
1900
            List<OrderHint> orderHints, List<String> propertyPaths,
1901
            Direction direction) {
1902

    
1903
        AuditEvent auditEvent = getAuditEventFromContext();
1904
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1905
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1906

    
1907
            if (direction.equals(Direction.relatedTo)){
1908
                criteria.add(Restrictions.eq("relatedTo", taxonBase));
1909
            }else{
1910
                criteria.add(Restrictions.eq("relatedFrom", taxonBase));
1911
            }
1912
            if(type != null) {
1913
                criteria.add(Restrictions.eq("type", type));
1914
            }
1915

    
1916
            addOrder(criteria,orderHints);
1917

    
1918
            if(pageSize != null) {
1919
                criteria.setMaxResults(pageSize);
1920
                if(pageNumber != null) {
1921
                    criteria.setFirstResult(pageNumber * pageSize);
1922
                } else {
1923
                    criteria.setFirstResult(0);
1924
                }
1925
            }
1926

    
1927
            List<SynonymRelationship> result = criteria.list();
1928
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1929

    
1930
            return result;
1931
        } else {
1932
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1933

    
1934
            if (direction.equals(Direction.relatedTo)){
1935
                query.add(AuditEntity.relatedId("relatedTo").eq(taxonBase.getId()));
1936
            }else{
1937
                query.add(AuditEntity.relatedId("relatedFrom").eq(taxonBase.getId()));
1938
            }
1939

    
1940
            if(type != null) {
1941
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1942
            }
1943

    
1944
            if(pageSize != null) {
1945
                query.setMaxResults(pageSize);
1946
                if(pageNumber != null) {
1947
                    query.setFirstResult(pageNumber * pageSize);
1948
                } else {
1949
                    query.setFirstResult(0);
1950
                }
1951
            }
1952

    
1953
            List<SynonymRelationship> result = query.getResultList();
1954
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1955

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

    
1965
            return result;
1966
        }
1967
    }
1968

    
1969

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

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

    
1996
        List<UuidAndTitleCache<TaxonBase>> result = getUuidAndTitleCache(query);
1997

    
1998
        return result;
1999
    }
2000
    @Override
2001
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym(Integer limit, String pattern){
2002

    
2003
        return getUuidAndTitleCache(limit, pattern, false);
2004
    }
2005

    
2006
    @Override
2007
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon(Integer limit, String pattern){
2008

    
2009
        return getUuidAndTitleCache(limit, pattern, true);
2010
    }
2011

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

    
2014
           boolean doAreaRestriction = areasExpanded.size() > 0;
2015
           String 	doAreaRestrictionSubSelect = "select %s.id from" +
2016
                " Distribution e" +
2017
                " join e.inDescription d" +
2018
                " join d.taxon t" +
2019
                (classification != null ? " join t.taxonNodes as tn " : " ");
2020

    
2021
           String 	doAreaRestrictionMisappliedNameSubSelect = "select %s.id from" +
2022
            " Distribution e" +
2023
            " join e.inDescription d" +
2024
            " join d.taxon t";
2025

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

    
2029
           String doTaxonNameJoin =   " join t.name n ";
2030

    
2031
           String doSynonymNameJoin =  	" join t.synonymRelations sr join sr.relatedFrom s join s.name sn";
2032

    
2033
           String doMisappliedNamesJoin = " left join t.relationsFromThisTaxon as rft" +
2034
                " left join rft.relatedTo as rt" +
2035
                (classification != null ? " left join rt.taxonNodes as tn2" : " ") +
2036
                " left join rt.name as n2" +
2037
                " left join rft.type as rtype";
2038

    
2039
           String doClassificationWhere = " tn.classification = :classification";
2040
           String doClassificationForMisappliedNamesWhere = " tn2 .classification = :classification";
2041

    
2042
           String doAreaRestrictionWhere =  " e.area.uuid in (:namedAreasUuids)";
2043

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

    
2046
           String doRelationshipTypeComparison = " rtype = :rType ";
2047

    
2048
        String taxonSubselect = null;
2049
        String synonymSubselect = null;
2050
        String misappliedSelect = null;
2051

    
2052
        if(classification != null ){
2053
            if (!doIncludeMisappliedNames){
2054
                if(doAreaRestriction){
2055
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2056
                    " WHERE " + doAreaRestrictionWhere +
2057
                    " AND " + doClassificationWhere +
2058
                    " AND " + String.format(doSearchFieldWhere, "n");
2059
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2060
                    " WHERE " + doAreaRestrictionWhere +
2061
                    " AND " + doClassificationWhere +
2062
                    " AND " + String.format(doSearchFieldWhere, "sn");
2063
                } else {
2064
                    taxonSubselect = String.format(doTaxonSubSelect, "t" )+ doTaxonNameJoin +
2065
                    " WHERE " + doClassificationWhere +
2066
                    " AND " + String.format(doSearchFieldWhere, "n");
2067
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
2068
                    " WHERE " + doClassificationWhere +
2069
                    " AND " + String.format(doSearchFieldWhere, "sn");
2070
                }
2071
            }else{ //misappliedNames included
2072
                if(doAreaRestriction){
2073
                    misappliedSelect = String.format(doAreaRestrictionMisappliedNameSubSelect, "t") + doTaxonNameJoin + doMisappliedNamesJoin +
2074
                    " WHERE " + doAreaRestrictionWhere +
2075
                    " AND " + String.format(doSearchFieldWhere, "n") +
2076
                    " AND " + doClassificationForMisappliedNamesWhere +
2077
                    " AND " + doRelationshipTypeComparison;
2078

    
2079
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2080
                    " WHERE " + doAreaRestrictionWhere +
2081
                    " AND "+ String.format(doSearchFieldWhere, "n") + " AND "+ doClassificationWhere;
2082

    
2083
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2084
                    " WHERE " + doAreaRestrictionWhere +
2085
                    " AND " + doClassificationWhere + " AND " +  String.format(doSearchFieldWhere, "sn");;
2086

    
2087
                } else {
2088
                    misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin +
2089
                    " WHERE " + String.format(doSearchFieldWhere, "n") +
2090
                    " AND " + doClassificationForMisappliedNamesWhere +
2091
                    " AND " + doRelationshipTypeComparison;
2092

    
2093
                    taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin +
2094
                    " WHERE " +  String.format(doSearchFieldWhere, "n") +
2095
                    " AND "+ doClassificationWhere;
2096

    
2097
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
2098
                    " WHERE " + doClassificationWhere +
2099
                    " AND " +  String.format(doSearchFieldWhere, "sn");
2100

    
2101
                }
2102
            }
2103
        } else {
2104
            if(doAreaRestriction){
2105
                misappliedSelect = String.format(doAreaRestrictionMisappliedNameSubSelect, "t") + doTaxonNameJoin + doMisappliedNamesJoin +
2106
                " WHERE " + doAreaRestrictionWhere +
2107
                " AND " + String.format(doSearchFieldWhere, "n")+
2108
                " AND " + doRelationshipTypeComparison;
2109

    
2110
                taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2111
                " WHERE " + doAreaRestrictionWhere +
2112
                " AND " + String.format(doSearchFieldWhere, "n");
2113

    
2114
                synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2115
                " WHERE " +   doAreaRestrictionWhere +
2116
                " AND " +  String.format(doSearchFieldWhere, "sn");
2117

    
2118

    
2119
            } else {
2120
                misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin + " WHERE " +  String.format(doSearchFieldWhere, "n") + " AND " + doRelationshipTypeComparison;
2121
                taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "n");
2122
                synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "sn");
2123

    
2124
            }
2125
        }
2126
        String[] result = {misappliedSelect, taxonSubselect, synonymSubselect};
2127

    
2128
        return result;
2129
    }
2130

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

    
2148
		return null;
2149
	}
2150

    
2151

    
2152
	/**
2153
	 * @param
2154
	 * @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)
2155
	 */
2156
	@Override
2157
	public <S extends TaxonBase> int countByIdentifier(Class<S> clazz,
2158
			String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter, MatchMode matchmode) {
2159
		if (subtreeFilter == null){
2160
			return countByIdentifier(clazz, identifier, identifierType, matchmode);
2161
		}
2162

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

    
2166
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
2167
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
2168

    
2169
		getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
2170
		String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
2171
		String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
2172
		String synTreeJoin = isSynonym ? " LEFT JOIN c.synonymRelations sr LEFT  JOIN sr.relatedTo as acc LEFT JOIN acc.taxonNodes synTn  " : "";
2173
		String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
2174
		String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
2175

    
2176
		String queryString = "SELECT count(*)  FROM %s as c " +
2177
                " INNER JOIN c.identifiers as ids " +
2178
                accTreeJoin +
2179
                synTreeJoin +
2180
                " WHERE (1=1) " +
2181
                	"  AND ( " + accWhere + " OR " + synWhere + ")";
2182
		queryString = String.format(queryString, clazzParam.getSimpleName());
2183

    
2184
		if (identifier != null){
2185
			if (matchmode == null || matchmode == MatchMode.EXACT){
2186
				queryString += " AND ids.identifier = '"  + identifier + "'";
2187
			}else {
2188
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
2189
			}
2190
		}
2191
		if (identifierType != null){
2192
        	queryString += " AND ids.type = :type";
2193
        }
2194

    
2195
		Query query = getSession().createQuery(queryString);
2196
        if (identifierType != null){
2197
        	query.setEntity("type", identifierType);
2198
        }
2199

    
2200
		Long c = (Long)query.uniqueResult();
2201
        return c.intValue();
2202
	}
2203

    
2204
	@Override
2205
	public <S extends TaxonBase> List<Object[]> findByIdentifier(
2206
			Class<S> clazz, String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter,
2207
			MatchMode matchmode, boolean includeEntity,
2208
			Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
2209

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

    
2213
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
2214
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
2215
		getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
2216
		String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
2217
		String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
2218
		String synTreeJoin = isSynonym ? " LEFT JOIN c.synonymRelations sr LEFT  JOIN sr.relatedTo as acc LEFT JOIN acc.taxonNodes synTn  " : "";
2219
		String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
2220
		String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
2221

    
2222
		String queryString = "SELECT ids.type, ids.identifier, %s " +
2223
				" FROM %s as c " +
2224
                " INNER JOIN c.identifiers as ids " +
2225
                accTreeJoin +
2226
				synTreeJoin +
2227
                " WHERE (1=1) " +
2228
                	" AND ( " + accWhere + " OR " + synWhere + ")";
2229
		queryString = String.format(queryString, (includeEntity ? "c":"c.uuid, c.titleCache") , clazzParam.getSimpleName());
2230

    
2231
		//Matchmode and identifier
2232
		if (identifier != null){
2233
			if (matchmode == null || matchmode == MatchMode.EXACT){
2234
				queryString += " AND ids.identifier = '"  + identifier + "'";
2235
			}else {
2236
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
2237
			}
2238
		}
2239
        if (identifierType != null){
2240
        	queryString += " AND ids.type = :type";
2241
        }
2242
        //order
2243
        queryString +=" ORDER BY ids.type.uuid, ids.identifier, c.uuid ";
2244

    
2245
		Query query = getSession().createQuery(queryString);
2246

    
2247
		//parameters
2248
		if (identifierType != null){
2249
        	query.setEntity("type", identifierType);
2250
        }
2251

    
2252
        //paging
2253
        setPagingParameter(query, pageSize, pageNumber);
2254

    
2255
        List<Object[]> results = query.list();
2256
        //initialize
2257
        if (includeEntity){
2258
        	List<S> entities = new ArrayList<S>();
2259
        	for (Object[] result : results){
2260
        		entities.add((S)result[2]);
2261
        	}
2262
        	defaultBeanInitializer.initializeAll(entities, propertyPaths);
2263
        }
2264
        return results;
2265
	}
2266

    
2267
}
(3-3/4)