Project

General

Profile

Download (95 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, String authorship, Rank rank, Integer pageSize,	Integer pageNumber) {
1217
        checkNotInPriorView("TaxonDaoHibernateImpl.findTaxaByName(Boolean accepted, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, String authorship, Rank rank, Integer pageSize,	Integer pageNumber)");
1218
        Criteria criteria = null;
1219
        if (clazz == null){
1220
            criteria = getSession().createCriteria(TaxonBase.class);
1221
        } else{
1222
            criteria = getSession().createCriteria(clazz);
1223
        }
1224
        criteria.setFetchMode( "name", FetchMode.JOIN );
1225
        criteria.createAlias("name", "name");
1226

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

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

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

    
1244
        }
1245

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

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

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

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

    
1271
        return criteria.list();
1272
    }
1273

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

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

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

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

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

    
1296
            addOrder(criteria,orderHints);
1297

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

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

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

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

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

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

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

    
1340
            return result;
1341
        }
1342
    }
1343

    
1344
    class TaxonRelationshipFromTaxonComparator implements Comparator<TaxonRelationship> {
1345

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

    
1351
    }
1352

    
1353
    class SynonymRelationshipFromTaxonComparator implements Comparator<SynonymRelationship> {
1354

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

    
1360
    }
1361

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

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

    
1373
            addOrder(criteria,orderHints);
1374

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

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

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

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

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

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

    
1408
            return result;
1409
        }
1410
    }
1411

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

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

    
1423
            addOrder(criteria,orderHints);
1424

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

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

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

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

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

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

    
1458
            return result;
1459
        }
1460
    }
1461

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

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

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

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

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

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

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

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

    
1509

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

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

    
1520
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1521

    
1522
        return result;
1523

    
1524
    }
1525

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

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

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

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

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

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

    
1541
        return count;
1542

    
1543
    }
1544

    
1545

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

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

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

    
1569
         String queryString = "SELECT nodes.uuid, nodes.id, taxon.titleCache FROM TaxonNode AS nodes JOIN nodes.taxon as taxon WHERE nodes.classification.id = " + classificationId ;
1570
         if ( excludeUuid != null &&  !excludeUuid.isEmpty()){
1571
             queryString = queryString + " AND taxon.uuid NOT IN (:excludeUuid)" ;
1572
         }
1573
         if (pattern != null){
1574
             pattern = pattern.replace("*", "%");
1575
             queryString = queryString + " AND taxon.titleCache like (:pattern)" ;
1576
         }
1577

    
1578
         Query query = getSession().createQuery(queryString);
1579

    
1580

    
1581
         if (limit != null){
1582
             query.setMaxResults(limit);
1583
         }
1584
         if ( excludeUuid != null &&  !excludeUuid.isEmpty()){
1585
             query.setParameterList("excludeUuid", excludeUuid);
1586
         }
1587
         if (pattern != null){
1588
             query.setParameter("pattern", pattern);
1589
         }
1590
         @SuppressWarnings("unchecked")
1591
         List<Object[]> result = query.list();
1592

    
1593
         if(result.size() == 0){
1594
             return null;
1595
         }else{
1596
             List<UuidAndTitleCache<TaxonNode>> list = new ArrayList<UuidAndTitleCache<TaxonNode>>(result.size());
1597

    
1598
             for (Object object : result){
1599

    
1600
                 Object[] objectArray = (Object[]) object;
1601

    
1602
                 UUID uuid = (UUID)objectArray[0];
1603
                 Integer id = (Integer) objectArray[1];
1604
                 String titleCache = (String) objectArray[2];
1605

    
1606
                 list.add(new UuidAndTitleCache<TaxonNode>(TaxonNode.class, uuid, id, titleCache));
1607
             }
1608

    
1609
             return list;
1610
         }
1611
    }
1612

    
1613

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

    
1634
            defaultBeanInitializer.initialize(taxonBase, propertyPaths);
1635
        }
1636
        return taxonBase;
1637
    }
1638

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

    
1646

    
1647

    
1648

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

    
1651
        for (SynonymRelationship synonymRelation:synonymRelationships){
1652
            TaxonNameBase synName;
1653
            NonViralName inferredSynName;
1654
            Synonym syn = synonymRelation.getSynonym();
1655
            HibernateProxyHelper.deproxy(syn);
1656

    
1657
            synName = syn.getName();
1658
            ZoologicalName zooName = zooHashMap.get(synName.getUuid());
1659
            String synGenusName = zooName.getGenusOrUninomial();
1660

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

    
1684

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

    
1699
    @Override
1700
    public int countAllRelationships() {
1701
        return countAllRelationships(null);
1702
    }
1703

    
1704

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

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

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

    
1741
        return taxonNames;
1742
    }
1743

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

    
1750
        return zooNames;
1751

    
1752
    }
1753

    
1754
    @Override
1755
    public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPaths) {
1756

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

    
1759
        List<TaxonNameBase> zooNames = query.list();
1760

    
1761
        TaxonNameComparator taxComp = new TaxonNameComparator();
1762
        Collections.sort(zooNames, taxComp);
1763

    
1764
        for (TaxonNameBase taxonNameBase: zooNames){
1765
            defaultBeanInitializer.initialize(taxonNameBase, propertyPaths);
1766
        }
1767

    
1768
        return zooNames;
1769
    }
1770

    
1771
    @Override
1772
    public List<TaxonNameBase> findIdenticalNamesNew(List<String> propertyPaths){
1773

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

    
1785
        //Get all names of erms
1786

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

    
1790
        List<String> namesErms = query.list();
1791
        /*TaxonNameComparator comp = new TaxonNameComparator();
1792
        Collections.sort(namesFauna);
1793
        Collections.sort(namesErms);
1794
        */
1795
        List <String> identicalNames = new ArrayList<String>();
1796
        String predecessor = "";
1797

    
1798
        for (String nameFauna: namesFauna){
1799
            if (namesErms.contains(nameFauna)){
1800
                identicalNames.add(nameFauna);
1801
            }
1802
        }
1803

    
1804

    
1805
        query = getSession().createQuery("from ZoologicalName zn where zn.nameCache IN (:identicalNames)");
1806
        query.setParameterList("identicalNames", identicalNames);
1807
        List<TaxonNameBase> result = query.list();
1808
        TaxonNameBase temp = result.get(0);
1809

    
1810
        Iterator<OriginalSourceBase> sources = temp.getSources().iterator();
1811

    
1812
        TaxonNameComparator taxComp = new TaxonNameComparator();
1813
        Collections.sort(result, taxComp);
1814
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1815
        return result;
1816

    
1817
    }
1818

    
1819

    
1820

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

    
1835

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

    
1851
    @Override
1852
    public long deleteSynonymRelationships(Synonym synonym, Taxon taxon) {
1853

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

    
1861
        q.setParameter("syn", synonym);
1862
        if (taxon != null){
1863
            q.setParameter("taxon", taxon);
1864
        }
1865
        long result = q.executeUpdate();
1866

    
1867
        return result;
1868
    }
1869

    
1870

    
1871
    @Override
1872
    public Integer countSynonymRelationships(TaxonBase taxonBase,
1873
            SynonymRelationshipType type, Direction relatedfrom) {
1874
        AuditEvent auditEvent = getAuditEventFromContext();
1875
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1876
            Query query = null;
1877

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

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

    
1892
            if(type != null) {
1893
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1894
            }
1895

    
1896
            return ((Long)query.getSingleResult()).intValue();
1897
        }
1898
    }
1899

    
1900

    
1901
    @Override
1902
    public List<SynonymRelationship> getSynonymRelationships(TaxonBase taxonBase,
1903
            SynonymRelationshipType type, Integer pageSize, Integer pageNumber,
1904
            List<OrderHint> orderHints, List<String> propertyPaths,
1905
            Direction direction) {
1906

    
1907
        AuditEvent auditEvent = getAuditEventFromContext();
1908
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1909
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1910

    
1911
            if (direction.equals(Direction.relatedTo)){
1912
                criteria.add(Restrictions.eq("relatedTo", taxonBase));
1913
            }else{
1914
                criteria.add(Restrictions.eq("relatedFrom", taxonBase));
1915
            }
1916
            if(type != null) {
1917
                criteria.add(Restrictions.eq("type", type));
1918
            }
1919

    
1920
            addOrder(criteria,orderHints);
1921

    
1922
            if(pageSize != null) {
1923
                criteria.setMaxResults(pageSize);
1924
                if(pageNumber != null) {
1925
                    criteria.setFirstResult(pageNumber * pageSize);
1926
                } else {
1927
                    criteria.setFirstResult(0);
1928
                }
1929
            }
1930

    
1931
            List<SynonymRelationship> result = criteria.list();
1932
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1933

    
1934
            return result;
1935
        } else {
1936
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1937

    
1938
            if (direction.equals(Direction.relatedTo)){
1939
                query.add(AuditEntity.relatedId("relatedTo").eq(taxonBase.getId()));
1940
            }else{
1941
                query.add(AuditEntity.relatedId("relatedFrom").eq(taxonBase.getId()));
1942
            }
1943

    
1944
            if(type != null) {
1945
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1946
            }
1947

    
1948
            if(pageSize != null) {
1949
                query.setMaxResults(pageSize);
1950
                if(pageNumber != null) {
1951
                    query.setFirstResult(pageNumber * pageSize);
1952
                } else {
1953
                    query.setFirstResult(0);
1954
                }
1955
            }
1956

    
1957
            List<SynonymRelationship> result = query.getResultList();
1958
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1959

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

    
1969
            return result;
1970
        }
1971
    }
1972

    
1973

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

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

    
2000
        List<UuidAndTitleCache<TaxonBase>> result = getUuidAndTitleCache(query);
2001

    
2002
        return result;
2003
    }
2004
    @Override
2005
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym(Integer limit, String pattern){
2006

    
2007
        return getUuidAndTitleCache(limit, pattern, false);
2008
    }
2009

    
2010
    @Override
2011
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon(Integer limit, String pattern){
2012

    
2013
        return getUuidAndTitleCache(limit, pattern, true);
2014
    }
2015

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

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

    
2025
           String 	doAreaRestrictionMisappliedNameSubSelect = "select %s.id from" +
2026
            " Distribution e" +
2027
            " join e.inDescription d" +
2028
            " join d.taxon t";
2029

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

    
2033
           String doTaxonNameJoin =   " join t.name n ";
2034

    
2035
           String doSynonymNameJoin =  	" join t.synonymRelations sr join sr.relatedFrom s join s.name sn";
2036

    
2037
           String doMisappliedNamesJoin = " left join t.relationsFromThisTaxon as rft" +
2038
                " left join rft.relatedTo as rt" +
2039
                (classification != null ? " left join rt.taxonNodes as tn2" : " ") +
2040
                " left join rt.name as n2" +
2041
                " left join rft.type as rtype";
2042

    
2043
           String doClassificationWhere = " tn.classification = :classification";
2044
           String doClassificationForMisappliedNamesWhere = " tn2 .classification = :classification";
2045

    
2046
           String doAreaRestrictionWhere =  " e.area.uuid in (:namedAreasUuids)";
2047

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

    
2050
           String doRelationshipTypeComparison = " rtype = :rType ";
2051

    
2052
        String taxonSubselect = null;
2053
        String synonymSubselect = null;
2054
        String misappliedSelect = null;
2055

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

    
2083
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2084
                    " WHERE " + doAreaRestrictionWhere +
2085
                    " AND "+ String.format(doSearchFieldWhere, "n") + " AND "+ doClassificationWhere;
2086

    
2087
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2088
                    " WHERE " + doAreaRestrictionWhere +
2089
                    " AND " + doClassificationWhere + " AND " +  String.format(doSearchFieldWhere, "sn");;
2090

    
2091
                } else {
2092
                    misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin +
2093
                    " WHERE " + String.format(doSearchFieldWhere, "n") +
2094
                    " AND " + doClassificationForMisappliedNamesWhere +
2095
                    " AND " + doRelationshipTypeComparison;
2096

    
2097
                    taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin +
2098
                    " WHERE " +  String.format(doSearchFieldWhere, "n") +
2099
                    " AND "+ doClassificationWhere;
2100

    
2101
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
2102
                    " WHERE " + doClassificationWhere +
2103
                    " AND " +  String.format(doSearchFieldWhere, "sn");
2104

    
2105
                }
2106
            }
2107
        } else {
2108
            if(doAreaRestriction){
2109
                misappliedSelect = String.format(doAreaRestrictionMisappliedNameSubSelect, "t") + doTaxonNameJoin + doMisappliedNamesJoin +
2110
                " WHERE " + doAreaRestrictionWhere +
2111
                " AND " + String.format(doSearchFieldWhere, "n")+
2112
                " AND " + doRelationshipTypeComparison;
2113

    
2114
                taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2115
                " WHERE " + doAreaRestrictionWhere +
2116
                " AND " + String.format(doSearchFieldWhere, "n");
2117

    
2118
                synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2119
                " WHERE " +   doAreaRestrictionWhere +
2120
                " AND " +  String.format(doSearchFieldWhere, "sn");
2121

    
2122

    
2123
            } else {
2124
                misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin + " WHERE " +  String.format(doSearchFieldWhere, "n") + " AND " + doRelationshipTypeComparison;
2125
                taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "n");
2126
                synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "sn");
2127

    
2128
            }
2129
        }
2130
        String[] result = {misappliedSelect, taxonSubselect, synonymSubselect};
2131

    
2132
        return result;
2133
    }
2134

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

    
2152
		return null;
2153
	}
2154

    
2155

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

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

    
2170
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
2171
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
2172

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

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

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

    
2199
		Query query = getSession().createQuery(queryString);
2200
        if (identifierType != null){
2201
        	query.setEntity("type", identifierType);
2202
        }
2203

    
2204
		Long c = (Long)query.uniqueResult();
2205
        return c.intValue();
2206
	}
2207

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

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

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

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

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

    
2249
		Query query = getSession().createQuery(queryString);
2250

    
2251
		//parameters
2252
		if (identifierType != null){
2253
        	query.setEntity("type", identifierType);
2254
        }
2255

    
2256
        //paging
2257
        setPagingParameter(query, pageSize, pageNumber);
2258

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

    
2271
}
(3-3/4)