Project

General

Profile

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

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

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

    
42
import eu.etaxonomy.cdm.model.common.CdmBase;
43
import eu.etaxonomy.cdm.model.common.DefinedTerm;
44
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
45
import eu.etaxonomy.cdm.model.common.LSID;
46
import eu.etaxonomy.cdm.model.common.OriginalSourceBase;
47
import eu.etaxonomy.cdm.model.common.RelationshipBase;
48
import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
49
import eu.etaxonomy.cdm.model.location.NamedArea;
50
import eu.etaxonomy.cdm.model.name.NonViralName;
51
import eu.etaxonomy.cdm.model.name.Rank;
52
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
53
import eu.etaxonomy.cdm.model.name.TaxonNameComparator;
54
import eu.etaxonomy.cdm.model.reference.Reference;
55
import eu.etaxonomy.cdm.model.taxon.Classification;
56
import eu.etaxonomy.cdm.model.taxon.Synonym;
57
import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
58
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
59
import eu.etaxonomy.cdm.model.taxon.Taxon;
60
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
61
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
62
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
63
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
64
import eu.etaxonomy.cdm.model.view.AuditEvent;
65
import eu.etaxonomy.cdm.persistence.dao.hibernate.common.IdentifiableDaoBase;
66
import eu.etaxonomy.cdm.persistence.dao.name.ITaxonNameDao;
67
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
68
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
69
import eu.etaxonomy.cdm.persistence.fetch.CdmFetch;
70
import eu.etaxonomy.cdm.persistence.query.MatchMode;
71
import eu.etaxonomy.cdm.persistence.query.OrderHint;
72

    
73

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

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

    
92
    @Autowired
93
    private ITaxonNameDao taxonNameDao;
94

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

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

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

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

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

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

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

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

    
140
        List<Taxon> results = new ArrayList<Taxon>();
141
        @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())));
286
                    }
287
                    else {
288
                        resultObjects.add( new UuidAndTitleCache(Taxon.class, (UUID) result[0], (Integer) result[1], (String)result[2], new Boolean(result[4].toString())));
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())));
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())));
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, Rank rank, Integer pageSize,	Integer pageNumber) {
1217
        checkNotInPriorView("TaxonDaoHibernateImpl.findTaxaByName(Boolean accepted, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, 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(rank != null) {
1253
            criteria.add(Restrictions.eq("name.rank", rank));
1254
        }
1255

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

    
1265
        return criteria.list();
1266
    }
1267

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

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

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

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

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

    
1290
            addOrder(criteria,orderHints);
1291

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

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

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

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

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

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

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

    
1334
            return result;
1335
        }
1336
    }
1337

    
1338
    class TaxonRelationshipFromTaxonComparator implements Comparator<TaxonRelationship> {
1339

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

    
1345
    }
1346

    
1347
    class SynonymRelationshipFromTaxonComparator implements Comparator<SynonymRelationship> {
1348

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

    
1354
    }
1355

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

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

    
1367
            addOrder(criteria,orderHints);
1368

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

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

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

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

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

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

    
1402
            return result;
1403
        }
1404
    }
1405

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

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

    
1417
            addOrder(criteria,orderHints);
1418

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

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

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

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

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

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

    
1452
            return result;
1453
        }
1454
    }
1455

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

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

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

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

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

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

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

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

    
1503

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

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

    
1514
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1515

    
1516
        return result;
1517

    
1518
    }
1519

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

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

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

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

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

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

    
1535
        return count;
1536

    
1537
    }
1538

    
1539

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

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

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

    
1563
         String queryString = "SELECT nodes.uuid, nodes.id, taxon.titleCache FROM TaxonNode AS nodes JOIN nodes.taxon as taxon WHERE nodes.classification.id = " + classificationId ;
1564
         if ( excludeUuid != null &&  !excludeUuid.isEmpty()){
1565
             queryString = queryString + " AND taxon.uuid NOT IN (:excludeUuid)" ;
1566
         }
1567
         if (pattern != null){
1568
             pattern = pattern.replace("*", "%");
1569
             queryString = queryString + " AND taxon.titleCache like (:pattern)" ;
1570
         }
1571

    
1572
         Query query = getSession().createQuery(queryString);
1573

    
1574

    
1575
         if (limit != null){
1576
             query.setMaxResults(limit);
1577
         }
1578
         if ( excludeUuid != null &&  !excludeUuid.isEmpty()){
1579
             query.setParameterList("excludeUuid", excludeUuid);
1580
         }
1581
         if (pattern != null){
1582
             query.setParameter("pattern", pattern);
1583
         }
1584
         @SuppressWarnings("unchecked")
1585
         List<Object[]> result = query.list();
1586

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

    
1592
             for (Object object : result){
1593

    
1594
                 Object[] objectArray = (Object[]) object;
1595

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

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

    
1603
             return list;
1604
         }
1605
    }
1606

    
1607

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

    
1628
            defaultBeanInitializer.initialize(taxonBase, propertyPaths);
1629
        }
1630
        return taxonBase;
1631
    }
1632

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

    
1640

    
1641

    
1642

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

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

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

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

    
1678

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

    
1693
    @Override
1694
    public int countAllRelationships() {
1695
        return countAllRelationships(null);
1696
    }
1697

    
1698

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

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

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

    
1735
        return taxonNames;
1736
    }
1737

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

    
1744
        return zooNames;
1745

    
1746
    }
1747

    
1748
    @Override
1749
    public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPaths) {
1750

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

    
1753
        List<TaxonNameBase> zooNames = query.list();
1754

    
1755
        TaxonNameComparator taxComp = new TaxonNameComparator();
1756
        Collections.sort(zooNames, taxComp);
1757

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

    
1762
        return zooNames;
1763
    }
1764

    
1765
    @Override
1766
    public List<TaxonNameBase> findIdenticalNamesNew(List<String> propertyPaths){
1767

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

    
1779
        //Get all names of erms
1780

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

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

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

    
1798

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

    
1804
        Iterator<OriginalSourceBase> sources = temp.getSources().iterator();
1805

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

    
1811
    }
1812

    
1813

    
1814

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

    
1829

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

    
1845
    @Override
1846
    public long deleteSynonymRelationships(Synonym synonym, Taxon taxon) {
1847

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

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

    
1861
        return result;
1862
    }
1863

    
1864

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

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

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

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

    
1890
            return ((Long)query.getSingleResult()).intValue();
1891
        }
1892
    }
1893

    
1894

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

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

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

    
1914
            addOrder(criteria,orderHints);
1915

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

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

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

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

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

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

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

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

    
1963
            return result;
1964
        }
1965
    }
1966

    
1967

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

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

    
1993
        List<UuidAndTitleCache<TaxonBase>> result = getUuidAndTitleCache(query);
1994

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

    
2000
        return getUuidAndTitleCache(limit, pattern, false);
2001
    }
2002

    
2003
    @Override
2004
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon(Integer limit, String pattern){
2005

    
2006
        return getUuidAndTitleCache(limit, pattern, true);
2007
    }
2008

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

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

    
2018
           String 	doAreaRestrictionMisappliedNameSubSelect = "select %s.id from" +
2019
            " Distribution e" +
2020
            " join e.inDescription d" +
2021
            " join d.taxon t";
2022

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

    
2026
           String doTaxonNameJoin =   " join t.name n ";
2027

    
2028
           String doSynonymNameJoin =  	" join t.synonymRelations sr join sr.relatedFrom s join s.name sn";
2029

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

    
2036
           String doClassificationWhere = " tn.classification = :classification";
2037
           String doClassificationForMisappliedNamesWhere = " tn2 .classification = :classification";
2038

    
2039
           String doAreaRestrictionWhere =  " e.area.uuid in (:namedAreasUuids)";
2040

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

    
2043
           String doRelationshipTypeComparison = " rtype = :rType ";
2044

    
2045
        String taxonSubselect = null;
2046
        String synonymSubselect = null;
2047
        String misappliedSelect = null;
2048

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

    
2076
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2077
                    " WHERE " + doAreaRestrictionWhere +
2078
                    " AND "+ String.format(doSearchFieldWhere, "n") + " AND "+ doClassificationWhere;
2079

    
2080
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2081
                    " WHERE " + doAreaRestrictionWhere +
2082
                    " AND " + doClassificationWhere + " AND " +  String.format(doSearchFieldWhere, "sn");;
2083

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

    
2090
                    taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin +
2091
                    " WHERE " +  String.format(doSearchFieldWhere, "n") +
2092
                    " AND "+ doClassificationWhere;
2093

    
2094
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
2095
                    " WHERE " + doClassificationWhere +
2096
                    " AND " +  String.format(doSearchFieldWhere, "sn");
2097

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

    
2107
                taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2108
                " WHERE " + doAreaRestrictionWhere +
2109
                " AND " + String.format(doSearchFieldWhere, "n");
2110

    
2111
                synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2112
                " WHERE " +   doAreaRestrictionWhere +
2113
                " AND " +  String.format(doSearchFieldWhere, "sn");
2114

    
2115

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

    
2121
            }
2122
        }
2123
        String[] result = {misappliedSelect, taxonSubselect, synonymSubselect};
2124

    
2125
        return result;
2126
    }
2127

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

    
2145
		return null;
2146
	}
2147

    
2148

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

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

    
2163
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
2164
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
2165

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

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

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

    
2192
		Query query = getSession().createQuery(queryString);
2193
        if (identifierType != null){
2194
        	query.setEntity("type", identifierType);
2195
        }
2196

    
2197
		Long c = (Long)query.uniqueResult();
2198
        return c.intValue();
2199
	}
2200

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

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

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

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

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

    
2242
		Query query = getSession().createQuery(queryString);
2243

    
2244
		//parameters
2245
		if (identifierType != null){
2246
        	query.setEntity("type", identifierType);
2247
        }
2248

    
2249
        //paging
2250
        setPagingParameter(query, pageSize, pageNumber);
2251

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

    
2264
}
(3-3/4)