Project

General

Profile

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

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

    
76

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

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

    
95
    @Autowired
96
    private ITaxonNameDao taxonNameDao;
97

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

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

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

    
122
        Criteria crit = getSession().createCriteria(Taxon.class);
123

    
124
        crit.setFetchMode("name", FetchMode.JOIN);
125
        crit.createAlias("name", "name");
126

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

    
133
        if (sec != null){
134
            crit.add(Restrictions.eq("sec", sec) );
135
        }
136

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

    
143
        List<Taxon> results = new ArrayList<Taxon>();
144
        List<Taxon> taxa = crit.list();
145
        for(Taxon taxon : taxa){
146

    
147

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

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

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

    
177
        return getTaxaByName(queryString, true, sec);
178
    }
179

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

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

    
191
        criteria.setFetchMode( "name", FetchMode.JOIN );
192
        criteria.createAlias("name", "name");
193

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

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

    
202
        return criteria.list();
203
    }
204

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

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

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

    
215
        boolean doTaxa = true;
216
        boolean doSynonyms = true;
217

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

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

    
231
        boolean doCount = false;
232

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

    
235
        if (query != null){
236
            List<TaxonBase> results = query.list();
237

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

    
244
        return new ArrayList<TaxonBase>();
245

    
246
    }
247

    
248

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

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

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

    
273

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

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

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

    
298
            return resultObjects;
299

    
300
        }
301

    
302
        return new ArrayList<UuidAndTitleCache<IdentifiableEntity>>();
303

    
304
    }
305

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

    
319
    }
320

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

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

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

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

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

    
380

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

    
386

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

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

    
402

    
403

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

    
417
                    } else {
418

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

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

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

    
480

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

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

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

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

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

    
568
                    } else {
569

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

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

    
586
                }
587

    
588
            }*/
589

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

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

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

    
606
                }
607

    
608

    
609
            }
610

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

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

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

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

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

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

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

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

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

    
727
            }
728

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

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

    
742

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

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

    
753

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

    
789
            return query;
790
    }
791

    
792

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

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

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

    
819
        return query;
820
    }
821

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

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

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

    
839
        query.setParameter("queryString", queryString);
840

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

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

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

    
865

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

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

    
892

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

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

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

    
907
        return criteria.list();
908
    }
909

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

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

    
919
        return criteria.list();
920
    }
921

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

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

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

    
934
            if (taxRelSize > start){
935

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

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

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

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

    
977
        taxonBase.removeSources();
978

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

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

    
993
       return super.delete(taxonBase);
994

    
995
    }
996

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

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

    
1009
    }
1010

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

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

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

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

    
1040
        return null;
1041
    }
1042

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

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

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

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

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

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

    
1077

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1199
        }
1200

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

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

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

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

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

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

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

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

    
1245
        }
1246

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

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

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

    
1266
        return criteria.list();
1267
    }
1268

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

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

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

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

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

    
1291
            addOrder(criteria,orderHints);
1292

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

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

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

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

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

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

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

    
1335
            return result;
1336
        }
1337
    }
1338

    
1339
    class TaxonRelationshipFromTaxonComparator implements Comparator<TaxonRelationship> {
1340

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

    
1346
    }
1347

    
1348
    class SynonymRelationshipFromTaxonComparator implements Comparator<SynonymRelationship> {
1349

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

    
1355
    }
1356

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

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

    
1368
            addOrder(criteria,orderHints);
1369

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

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

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

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

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

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

    
1403
            return result;
1404
        }
1405
    }
1406

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

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

    
1418
            addOrder(criteria,orderHints);
1419

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

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

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

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

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

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

    
1453
            return result;
1454
        }
1455
    }
1456

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

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

    
1468
    @Override
1469
    public String suggestQuery(String queryString) {
1470
        checkNotInPriorView("TaxonDaoHibernateImpl.suggestQuery(String queryString)");
1471
        String alternativeQueryString = null;
1472
        if (alternativeSpellingSuggestionParser != null) {
1473
            try {
1474

    
1475
                alternativeSpellingSuggestionParser.parse(queryString);
1476
                org.apache.lucene.search.Query alternativeQuery = alternativeSpellingSuggestionParser.suggest(queryString);
1477
                if (alternativeQuery != null) {
1478
                    alternativeQueryString = alternativeQuery
1479
                            .toString("name.titleCache");
1480
                }
1481

    
1482
            } catch (ParseException e) {
1483
                throw new QueryParseException(e, queryString);
1484
            }
1485
        }
1486
        return alternativeQueryString;
1487
    }
1488

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

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

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

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

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

    
1503

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

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

    
1514
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1515

    
1516
        return result;
1517

    
1518
    }
1519

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

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

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

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

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

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

    
1535
        return count;
1536

    
1537
    }
1538

    
1539

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

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

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

    
1559

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

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

    
1566

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

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

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

    
1579

    
1580

    
1581

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

    
1587
            for (Object object : result){
1588

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

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

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

    
1598
            return list;
1599
        }
1600
    }
1601

    
1602

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

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

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

    
1635

    
1636

    
1637

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

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

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

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

    
1673

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

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

    
1693

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

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

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

    
1730
        return taxonNames;
1731
    }
1732

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

    
1739
        return zooNames;
1740

    
1741
    }
1742

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

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

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

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

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

    
1757
        return zooNames;
1758
    }
1759

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

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

    
1774
        //Get all names of erms
1775

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

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

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

    
1793

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

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

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

    
1806
    }
1807

    
1808

    
1809

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

    
1824

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

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

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

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

    
1856
        return result;
1857
    }
1858

    
1859

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

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

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

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

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

    
1889

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

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

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

    
1909
            addOrder(criteria,orderHints);
1910

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

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

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

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

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

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

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

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

    
1958
            return result;
1959
        }
1960
    }
1961

    
1962

    
1963
    @Override
1964
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {
1965
        String queryString = String.format("select uuid, id, titleCache from %s where DTYPE = '%s'", type.getSimpleName(), Taxon.class.getSimpleName());
1966
        Query query = getSession().createQuery(queryString);
1967

    
1968
        List<UuidAndTitleCache<TaxonBase>> result = getUuidAndTitleCache(query);
1969

    
1970
        return result;
1971
    }
1972

    
1973
    @Override
1974
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {
1975
        String queryString = String.format("select uuid, id, titleCache from %s where DTYPE = '%s'", type.getSimpleName(), Synonym.class.getSimpleName());
1976
        Query query = getSession().createQuery(queryString);
1977

    
1978
        List<UuidAndTitleCache<TaxonBase>> result = getUuidAndTitleCache(query);
1979

    
1980
        return result;
1981
    }
1982

    
1983

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

    
1986
           boolean doAreaRestriction = areasExpanded.size() > 0;
1987
           String 	doAreaRestrictionSubSelect = "select %s.id from" +
1988
                " Distribution e" +
1989
                " join e.inDescription d" +
1990
                " join d.taxon t" +
1991
                (classification != null ? " join t.taxonNodes as tn " : " ");
1992

    
1993
           String 	doAreaRestrictionMisappliedNameSubSelect = "select %s.id from" +
1994
            " Distribution e" +
1995
            " join e.inDescription d" +
1996
            " join d.taxon t";
1997

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

    
2001
           String doTaxonNameJoin =   " join t.name n ";
2002

    
2003
           String doSynonymNameJoin =  	" join t.synonymRelations sr join sr.relatedFrom s join s.name sn";
2004

    
2005
           String doMisappliedNamesJoin = " left join t.relationsFromThisTaxon as rft" +
2006
                " left join rft.relatedTo as rt" +
2007
                (classification != null ? " left join rt.taxonNodes as tn2" : " ") +
2008
                " left join rt.name as n2" +
2009
                " left join rft.type as rtype";
2010

    
2011
           String doClassificationWhere = " tn.classification = :classification";
2012
           String doClassificationForMisappliedNamesWhere = " tn2 .classification = :classification";
2013

    
2014
           String doAreaRestrictionWhere =  " e.area.uuid in (:namedAreasUuids)";
2015

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

    
2018
           String doRelationshipTypeComparison = " rtype = :rType ";
2019

    
2020
        String taxonSubselect = null;
2021
        String synonymSubselect = null;
2022
        String misappliedSelect = null;
2023

    
2024
        if(classification != null ){
2025
            if (!doIncludeMisappliedNames){
2026
                if(doAreaRestriction){
2027
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2028
                    " WHERE " + doAreaRestrictionWhere +
2029
                    " AND " + doClassificationWhere +
2030
                    " AND " + String.format(doSearchFieldWhere, "n");
2031
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2032
                    " WHERE " + doAreaRestrictionWhere +
2033
                    " AND " + doClassificationWhere +
2034
                    " AND " + String.format(doSearchFieldWhere, "sn");
2035
                } else {
2036
                    taxonSubselect = String.format(doTaxonSubSelect, "t" )+ doTaxonNameJoin +
2037
                    " WHERE " + doClassificationWhere +
2038
                    " AND " + String.format(doSearchFieldWhere, "n");
2039
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
2040
                    " WHERE " + doClassificationWhere +
2041
                    " AND " + String.format(doSearchFieldWhere, "sn");
2042
                }
2043
            }else{ //misappliedNames included
2044
                if(doAreaRestriction){
2045
                    misappliedSelect = String.format(doAreaRestrictionMisappliedNameSubSelect, "t") + doTaxonNameJoin + doMisappliedNamesJoin +
2046
                    " WHERE " + doAreaRestrictionWhere +
2047
                    " AND " + String.format(doSearchFieldWhere, "n") +
2048
                    " AND " + doClassificationForMisappliedNamesWhere +
2049
                    " AND " + doRelationshipTypeComparison;
2050

    
2051
                    taxonSubselect = String.format(doAreaRestrictionSubSelect, "t") + doTaxonNameJoin +
2052
                    " WHERE " + doAreaRestrictionWhere +
2053
                    " AND "+ String.format(doSearchFieldWhere, "n") + " AND "+ doClassificationWhere;
2054

    
2055
                    synonymSubselect = String.format(doAreaRestrictionSubSelect, "s") + doSynonymNameJoin +
2056
                    " WHERE " + doAreaRestrictionWhere +
2057
                    " AND " + doClassificationWhere + " AND " +  String.format(doSearchFieldWhere, "sn");;
2058

    
2059
                } else {
2060
                    misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin +
2061
                    " WHERE " + String.format(doSearchFieldWhere, "n") +
2062
                    " AND " + doClassificationForMisappliedNamesWhere +
2063
                    " AND " + doRelationshipTypeComparison;
2064

    
2065
                    taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin +
2066
                    " WHERE " +  String.format(doSearchFieldWhere, "n") +
2067
                    " AND "+ doClassificationWhere;
2068

    
2069
                    synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin +
2070
                    " WHERE " + doClassificationWhere +
2071
                    " AND " +  String.format(doSearchFieldWhere, "sn");
2072

    
2073
                }
2074
            }
2075
        } else {
2076
            if(doAreaRestriction){
2077
                misappliedSelect = String.format(doAreaRestrictionMisappliedNameSubSelect, "t") + doTaxonNameJoin + doMisappliedNamesJoin +
2078
                " WHERE " + doAreaRestrictionWhere +
2079
                " AND " + String.format(doSearchFieldWhere, "n")+
2080
                " AND " + doRelationshipTypeComparison;
2081

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

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

    
2090

    
2091
            } else {
2092
                misappliedSelect = String.format(doTaxonMisappliedNameSubSelect, "t" ) + doTaxonNameJoin + doMisappliedNamesJoin + " WHERE " +  String.format(doSearchFieldWhere, "n") + " AND " + doRelationshipTypeComparison;
2093
                taxonSubselect = String.format(doTaxonSubSelect, "t" ) + doTaxonNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "n");
2094
                synonymSubselect = String.format(doTaxonSubSelect, "s" ) + doSynonymNameJoin + " WHERE " +  String.format(doSearchFieldWhere, "sn");
2095

    
2096
            }
2097
        }
2098
        String[] result = {misappliedSelect, taxonSubselect, synonymSubselect};
2099

    
2100
        return result;
2101
    }
2102

    
2103
	@Override
2104
	public List<UuidAndTitleCache<IdentifiableEntity>> getTaxaByCommonNameForEditor(
2105
			String titleSearchStringSqlized, Classification classification,
2106
			MatchMode matchMode, Set namedAreas) {
2107
	    List<Object> resultArray = new ArrayList<Object>();
2108
		Query query = prepareTaxaByCommonName(titleSearchStringSqlized, classification, matchMode, namedAreas, null, null, false, true);
2109
        if (query != null){
2110
            resultArray = query.list();
2111
            List<UuidAndTitleCache<IdentifiableEntity>> returnResult = new ArrayList<UuidAndTitleCache<IdentifiableEntity>>() ;
2112
            Object[] result;
2113
            for(int i = 0; i<resultArray.size();i++){
2114
            	result = (Object[]) resultArray.get(i);
2115
            	returnResult.add(new UuidAndTitleCache(Taxon.class, (UUID) result[0],(Integer)result[1], (String)result[2], new Boolean(result[4].toString())));
2116
            }
2117
            return returnResult;
2118
        }
2119

    
2120
		return null;
2121
	}
2122

    
2123

    
2124
	/**
2125
	 * @param
2126
	 * @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)
2127
	 */
2128
	@Override
2129
	public <S extends TaxonBase> int countByIdentifier(Class<S> clazz,
2130
			String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter, MatchMode matchmode) {
2131
		if (subtreeFilter == null){
2132
			return countByIdentifier(clazz, identifier, identifierType, matchmode);
2133
		}
2134

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

    
2138
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
2139
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
2140

    
2141
		getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
2142
		String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
2143
		String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
2144
		String synTreeJoin = isSynonym ? " LEFT JOIN c.synonymRelations sr LEFT  JOIN sr.relatedTo as acc LEFT JOIN acc.taxonNodes synTn  " : "";
2145
		String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
2146
		String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
2147

    
2148
		String queryString = "SELECT count(*)  FROM %s as c " +
2149
                " INNER JOIN c.identifiers as ids " +
2150
                accTreeJoin +
2151
                synTreeJoin +
2152
                " WHERE (1=1) " +
2153
                	"  AND ( " + accWhere + " OR " + synWhere + ")";
2154
		queryString = String.format(queryString, clazzParam.getSimpleName());
2155

    
2156
		if (identifier != null){
2157
			if (matchmode == null || matchmode == MatchMode.EXACT){
2158
				queryString += " AND ids.identifier = '"  + identifier + "'";
2159
			}else {
2160
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
2161
			}
2162
		}
2163
		if (identifierType != null){
2164
        	queryString += " AND ids.type = :type";
2165
        }
2166

    
2167
		Query query = getSession().createQuery(queryString);
2168
        if (identifierType != null){
2169
        	query.setEntity("type", identifierType);
2170
        }
2171

    
2172
		Long c = (Long)query.uniqueResult();
2173
        return c.intValue();
2174
	}
2175

    
2176
	@Override
2177
	public <S extends TaxonBase> List<Object[]> findByIdentifier(
2178
			Class<S> clazz, String identifier, DefinedTerm identifierType, TaxonNode subtreeFilter,
2179
			MatchMode matchmode, boolean includeEntity,
2180
			Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
2181

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

    
2185
		boolean isTaxon = clazzParam == Taxon.class || clazzParam == TaxonBase.class;
2186
		boolean isSynonym = clazzParam == Synonym.class || clazzParam == TaxonBase.class;
2187
		getSession().update(subtreeFilter);  //to avoid LIE when retrieving treeindex
2188
		String filterStr = "'" + subtreeFilter.treeIndex() + "%%'";
2189
		String accTreeJoin = isTaxon? " LEFT JOIN c.taxonNodes tn  " : "";
2190
		String synTreeJoin = isSynonym ? " LEFT JOIN c.synonymRelations sr LEFT  JOIN sr.relatedTo as acc LEFT JOIN acc.taxonNodes synTn  " : "";
2191
		String accWhere = isTaxon ?  "tn.treeIndex like " + filterStr : "(1=0)";
2192
		String synWhere = isSynonym  ?  "synTn.treeIndex like " + filterStr : "(1=0)";
2193

    
2194
		String queryString = "SELECT ids.type, ids.identifier, %s " +
2195
				" FROM %s as c " +
2196
                " INNER JOIN c.identifiers as ids " +
2197
                accTreeJoin +
2198
				synTreeJoin +
2199
                " WHERE (1=1) " +
2200
                	" AND ( " + accWhere + " OR " + synWhere + ")";
2201
		queryString = String.format(queryString, (includeEntity ? "c":"c.uuid, c.titleCache") , clazzParam.getSimpleName());
2202

    
2203
		//Matchmode and identifier
2204
		if (identifier != null){
2205
			if (matchmode == null || matchmode == MatchMode.EXACT){
2206
				queryString += " AND ids.identifier = '"  + identifier + "'";
2207
			}else {
2208
				queryString += " AND ids.identifier LIKE '" + matchmode.queryStringFrom(identifier)  + "'";
2209
			}
2210
		}
2211
        if (identifierType != null){
2212
        	queryString += " AND ids.type = :type";
2213
        }
2214
        //order
2215
        queryString +=" ORDER BY ids.type.uuid, ids.identifier, c.uuid ";
2216

    
2217
		Query query = getSession().createQuery(queryString);
2218

    
2219
		//parameters
2220
		if (identifierType != null){
2221
        	query.setEntity("type", identifierType);
2222
        }
2223

    
2224
        //paging
2225
        setPagingParameter(query, pageSize, pageNumber);
2226

    
2227
        List<Object[]> results = query.list();
2228
        //initialize
2229
        if (includeEntity){
2230
        	List<S> entities = new ArrayList<S>();
2231
        	for (Object[] result : results){
2232
        		entities.add((S)result[2]);
2233
        	}
2234
        	defaultBeanInitializer.initializeAll(entities, propertyPaths);
2235
        }
2236
        return results;
2237
	}
2238

    
2239
    /* (non-Javadoc)
2240
     * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(eu.etaxonomy.cdm.model.taxon.Classification)
2241
     */
2242
    @Override
2243
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
2244
            Classification classification) {
2245

    
2246
        return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification,null);
2247
    }
2248
}
(3-3/4)