Project

General

Profile

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

    
74

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

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

    
93
    @Autowired
94
    private ITaxonNameDao taxonNameDao;
95

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

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

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

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

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

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

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

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

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

    
145

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

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

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

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

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

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

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

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

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

    
200
        return criteria.list();
201
    }
202

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

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

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

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

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

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

    
229
        boolean doCount = false;
230

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

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

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

    
242
        return new ArrayList<TaxonBase>();
243

    
244
    }
245

    
246

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

    
255
        boolean doCount = false;
256
        Query query = prepareTaxaByNameForEditor(doTaxa, doSynonyms, "nameCache", queryString, classification, matchMode, namedAreas, doCount);
257

    
258

    
259
        if (query != null){
260
            List<Object[]> results = query.list();
261

    
262
            List<UuidAndTitleCache<TaxonBase>> resultObjects = new ArrayList<UuidAndTitleCache<TaxonBase>>();
263
            Object[] result;
264
            for(int i = 0; i<results.size();i++){
265
                result = results.get(i);
266

    
267
                //differentiate taxa and synonyms
268
                // new Boolean(result[3].toString()) is due to the fact that result[3] could be a Boolean ora String
269
                // see FIXME in 'prepareQuery' for more details
270
                if (doTaxa && doSynonyms){
271
                    if (result[2].equals("synonym")) {
272
                        resultObjects.add( new UuidAndTitleCache(Synonym.class, (UUID) result[0], (String)result[1], new Boolean(result[3].toString())));
273
                    }
274
                    else {
275
                        resultObjects.add( new UuidAndTitleCache(Taxon.class, (UUID) result[0], (String)result[1], new Boolean(result[3].toString())));
276
                    }
277
                }else if (doTaxa){
278
                        resultObjects.add( new UuidAndTitleCache(Taxon.class, (UUID) result[0], (String)result[1], new Boolean(result[3].toString())));
279
                }else if (doSynonyms){
280
                    resultObjects.add( new UuidAndTitleCache(Synonym.class, (UUID) result[0], (String)result[1], new Boolean(result[3].toString())));
281
                }
282
            }
283

    
284
            return resultObjects;
285

    
286
        }
287
        return new ArrayList<UuidAndTitleCache<TaxonBase>>();
288

    
289
    }
290

    
291
    @Override
292
    public List<Taxon> getTaxaByCommonName(String queryString, Classification classification,
293
               MatchMode matchMode, Set<NamedArea> namedAreas, Integer pageSize,
294
               Integer pageNumber, List<String> propertyPaths) {
295
        boolean doCount = false;
296
        Query query = prepareTaxaByCommonName(queryString, classification, matchMode, namedAreas, pageSize, pageNumber, doCount);
297
        if (query != null){
298
            List<Taxon> results = query.list();
299
            defaultBeanInitializer.initializeAll(results, propertyPaths);
300
            return results;
301
        }
302
        return new ArrayList<Taxon>();
303

    
304
    }
305

    
306
    /**
307
     * @param clazz
308
     * @param searchField the field in TaxonNameBase to be searched through usually either <code>nameCache</code> or <code>titleCache</code>
309
     * @param queryString
310
     * @param classification TODO
311
     * @param matchMode
312
     * @param namedAreas
313
     * @param pageSize
314
     * @param pageNumber
315
     * @param doCount
316
     * @return
317
     *
318
     *
319
     */
320
    private Query prepareTaxaByNameForEditor(boolean doTaxa, boolean doSynonyms, String searchField, String queryString, Classification classification,
321
            MatchMode matchMode, Set<NamedArea> namedAreas, boolean doCount) {
322
        return prepareQuery(doTaxa, doSynonyms, false, searchField, queryString,
323
                classification, matchMode, namedAreas, doCount, true);
324
    }
325

    
326
    /**
327
     * @param searchField
328
     * @param queryString
329
     * @param classification
330
     * @param matchMode
331
     * @param namedAreas
332
     * @param doCount
333
     * @param doNotReturnFullEntities
334
     *            if set true the seach method will not return synonym and taxon
335
     *            entities but an array containing the uuid, titleCache, and the
336
     *            DTYPE in lowercase letters.
337
     * @param clazz
338
     * @return
339
     */
340
    private Query prepareQuery(boolean doTaxa, boolean doSynonyms, boolean doIncludeMisappliedNames, String searchField, String queryString,
341
                Classification classification, MatchMode matchMode, Set<NamedArea> namedAreas, boolean doCount, boolean doNotReturnFullEntities){
342

    
343
            String hqlQueryString = matchMode.queryStringFrom(queryString);
344
            String selectWhat;
345
            if (doNotReturnFullEntities){
346
                selectWhat = "t.uuid, t.titleCache ";
347
            }else {
348
                selectWhat = (doCount ? "count(t)": "t");
349
            }
350

    
351
            String hql = "";
352
            Set<NamedArea> areasExpanded = new HashSet<NamedArea>();
353
            if(namedAreas != null && namedAreas.size() > 0){
354
                // expand areas and restrict by distribution area
355
                Query areaQuery = getSession().createQuery("select childArea from NamedArea as childArea left join childArea.partOf as parentArea where parentArea = :area");
356
                expandNamedAreas(namedAreas, areasExpanded, areaQuery);
357
            }
358
            boolean doAreaRestriction = areasExpanded.size() > 0;
359

    
360
            Set<UUID> namedAreasUuids = new HashSet<UUID>();
361
            for (NamedArea area:areasExpanded){
362
                namedAreasUuids.add(area.getUuid());
363
            }
364

    
365

    
366
            String [] subSelects = createHQLString(doTaxa, doSynonyms, doIncludeMisappliedNames, classification, areasExpanded, matchMode, searchField);
367
            String taxonSubselect = subSelects[1];
368
            String synonymSubselect = subSelects[2];
369
            String misappliedSelect = subSelects[0];
370

    
371

    
372
            /*if(classification != null ){
373
                if (!doIncludeMisappliedNames){
374
                    if(doAreaRestriction){
375

    
376
                        taxonSubselect = "select t.id from" +
377
                            " Distribution e" +
378
                            " join e.inDescription d" +
379
                            " join d.taxon t" +
380
                            " join t.name n " +
381
                            " join t.taxonNodes as tn "+
382
                            " where" +
383
                            " e.area.uuid in (:namedAreasUuids) AND" +
384
                            " tn.classification = :classification" +
385
                            " AND n." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
386

    
387

    
388

    
389
                        synonymSubselect = "select s.id from" +
390
                            " Distribution e" +
391
                            " join e.inDescription d" +
392
                            " join d.taxon t" + // the taxa
393
                            " join t.taxonNodes as tn "+
394
                            " join t.synonymRelations sr" +
395
                            " join sr.relatedFrom s" + // the synonyms
396
                            " join s.name sn"+
397
                            " where" +
398
                            " e.area.uuid in (:namedAreasUuids) AND" +
399
                            " tn.classification = :classification" +
400
                            " AND sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
401

    
402
                    } else {
403

    
404
                        taxonSubselect = "select t.id from" +
405
                            " Taxon t" +
406
                            " join t.name n " +
407
                            " join t.taxonNodes as tn "+
408
                            " where" +
409
                            " tn.classification = :classification" +
410
                            " AND n." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
411

    
412
                        synonymSubselect = "select s.id from" +
413
                            " Taxon t" + // the taxa
414
                            " join t.taxonNodes as tn "+
415
                            " join t.synonymRelations sr" +
416
                            " join sr.relatedFrom s" + // the synonyms
417
                            " join s.name sn"+
418
                            " where" +
419
                            " tn.classification = :classification" +
420
                            " AND sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
421
                    }
422
                }else{
423
                    if(doAreaRestriction){
424
                        if (!doTaxa && !doSynonyms ){
425
                            misappliedSelect = "select t.id from" +
426
                            " Distribution e" +
427
                            " join e.inDescription d" +
428
                            " join d.taxon t" +
429
                            " join t.name n " +
430
                            " join t.taxonNodes as tn "+
431
                            " left join t.relationsFromThisTaxon as rft" +
432
                            " left join rft.relatedTo as rt" +
433
                            " left join rt.taxonNodes as tn2" +
434
                            " left join rt.name as n2" +
435
                            " left join rft.type as rtype"+
436
                            " where" +
437
                            " e.area.uuid in (:namedAreasUuids) AND" +
438
                            " (tn.classification != :classification" +
439
                            " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString" +
440
                            " AND tn2.classification = :classification" +
441
                            " AND rtype = :rType )";
442

    
443
                        }else{
444
                            taxonSubselect = "select t.id from" +
445
                                " Distribution e" +
446
                                " join e.inDescription d" +
447
                                " join d.taxon t" +
448
                                " join t.name n " +
449
                                " join t.taxonNodes as tn "+
450
                                " left join t.relationsFromThisTaxon as rft" +
451
                                " left join rft.relatedTo as rt" +
452
                                " left join rt.taxonNodes as tn2" +
453
                                " left join rt.name as n2" +
454
                                " left join rft.type as rtype"+
455
                                " where" +
456
                                " e.area.uuid in (:namedAreasUuids) AND" +
457
                                " (tn.classification = :classification" +
458
                                " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString )" +
459
                                " OR"+
460
                                " (tn.classification != :classification" +
461
                                " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString" +
462
                                " AND tn2.classification = :classification" +
463
                                " AND rtype = :rType )";
464

    
465

    
466
                            synonymSubselect = "select s.id from" +
467
                                " Distribution e" +
468
                                " join e.inDescription d" +
469
                                " join d.taxon t" + // the taxa
470
                                " join t.taxonNodes as tn "+
471
                                " join t.synonymRelations sr" +
472
                                " join sr.relatedFrom s" + // the synonyms
473
                                " join s.name sn"+
474
                                " where" +
475
                                " e.area.uuid in (:namedAreasUuids) AND" +
476
                                " tn.classification != :classification" +
477
                                " AND sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
478
                        }
479
                    } else {
480
                        if (!doTaxa && !doSynonyms ){
481
                            misappliedSelect = "select t.id from" +
482
                            " Distribution e" +
483
                            " join e.inDescription d" +
484
                            " join d.taxon t" +
485
                            " join t.name n " +
486
                            " join t.taxonNodes as tn "+
487
                            " left join t.relationsFromThisTaxon as rft" +
488
                            " left join rft.relatedTo as rt" +
489
                            " left join rt.taxonNodes as tn2" +
490
                            " left join rt.name as n2" +
491
                            " left join rft.type as rtype"+
492
                            " where" +
493
                            " (tn.classification != :classification" +
494
                            " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString" +
495
                            " AND tn2.classification = :classification" +
496
                            " AND rtype = :rType )";
497

    
498
                        }else{
499
                            taxonSubselect = "select t.id from" +
500
                                " Taxon t" +
501
                                " join t.name n " +
502
                                " join t.taxonNodes as tn "+
503
                                " left join t.relationsFromThisTaxon as rft" +
504
                                " left join rft.relatedTo as rt" +
505
                                " left join rt.taxonNodes as tn2" +
506
                                " left join rt.name as n2" +
507
                                " left join rft.type as rtype"+
508
                                " where " +
509
                                " (tn.classification = :classification" +
510
                                " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString )" +
511
                                " OR"+
512
                                " (tn.classification != :classification" +
513
                                " AND n." + searchField + " " + matchMode.getMatchOperator() + " :queryString" +
514
                                " AND tn2.classification = :classification" +
515
                                " AND rtype = :rType )";
516

    
517
                            synonymSubselect = "select s.id from" +
518
                                " Taxon t" + // the taxa
519
                                " join t.taxonNodes as tn "+
520
                                " join t.synonymRelations sr" +
521
                                " join sr.relatedFrom s" + // the synonyms
522
                                " join s.name sn"+
523
                                " where" +
524
                                " tn.classification != :classification" +
525
                                " AND sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
526
                        }
527
                    }
528
                }
529
            } else {
530

    
531
                if (!doIncludeMisappliedNames){
532
                    if(doAreaRestriction){
533
                        taxonSubselect = "select t.id from " +
534
                            " Distribution e" +
535
                            " join e.inDescription d" +
536
                            " join d.taxon t" +
537
                            " join t.name n "+
538
                            " where" +
539
                            (doAreaRestriction ? " e.area.uuid in (:namedAreasUuids) AND" : "") +
540
                            " n." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
541

    
542
                        synonymSubselect = "select s.id from" +
543
                            " Distribution e" +
544
                            " join e.inDescription d" +
545
                            " join d.taxon t" + // the taxa
546
                            " join t.synonymRelations sr" +
547
                            " join sr.relatedFrom s" + // the synonyms
548
                            " join s.name sn"+
549
                            " where" +
550
                            (doAreaRestriction ? " e.area.uuid in (:namedAreasUuids) AND" : "") +
551
                            " sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
552

    
553
                    } else {
554

    
555
                        taxonSubselect = "select t.id from " +
556
                            " Taxon t" +
557
                            " join t.name n "+
558
                            " where" +
559
                            " n." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
560

    
561
                        synonymSubselect = "select s.id from" +
562
                            " Taxon t" + // the taxa
563
                            " join t.synonymRelations sr" +
564
                            " join sr.relatedFrom s" + // the synonyms
565
                            " join s.name sn"+
566
                            " where" +
567
                            " sn." + searchField +  " " + matchMode.getMatchOperator() + " :queryString";
568
                    }
569
                }else{
570

    
571
                }
572

    
573
            }*/
574

    
575
            logger.debug("taxonSubselect: " + taxonSubselect != null ? taxonSubselect: "NULL");
576
            logger.debug("synonymSubselect: " + synonymSubselect != null ? synonymSubselect: "NULL");
577

    
578
            Query subTaxon = null;
579
            Query subSynonym = null;
580
            Query subMisappliedNames = null;
581
            if(doTaxa){
582
                // find Taxa
583
                subTaxon = getSession().createQuery(taxonSubselect).setParameter("queryString", hqlQueryString);
584

    
585
                if(doAreaRestriction){
586
                    subTaxon.setParameterList("namedAreasUuids", namedAreasUuids);
587
                }
588
                if(classification != null){
589
                    subTaxon.setParameter("classification", classification);
590

    
591
                }
592

    
593

    
594
            }
595

    
596
            if(doSynonyms){
597
                // find synonyms
598
                subSynonym = getSession().createQuery(synonymSubselect).setParameter("queryString", hqlQueryString);
599

    
600
                if(doAreaRestriction){
601
                    subSynonym.setParameterList("namedAreasUuids", namedAreasUuids);
602
                }
603
                if(classification != null){
604
                    subSynonym.setParameter("classification", classification);
605
                }
606
            }
607
            if (doIncludeMisappliedNames ){
608
                subMisappliedNames = getSession().createQuery(misappliedSelect).setParameter("queryString", hqlQueryString);
609
                subMisappliedNames.setParameter("rType", TaxonRelationshipType.MISAPPLIED_NAME_FOR());
610
                if(doAreaRestriction){
611
                    subMisappliedNames.setParameterList("namedAreasUuids", namedAreasUuids);
612
                }
613
                if(classification != null){
614
                    subMisappliedNames.setParameter("classification", classification);
615
                }
616
            }
617

    
618
            List<Integer> taxa = new ArrayList<Integer>();
619
            List<Integer> synonyms = new ArrayList<Integer>();
620
            if (doSynonyms){
621
                synonyms = subSynonym.list();
622
            }
623
            if(doTaxa){
624
                taxa = subTaxon.list();
625
            }
626
            if (doIncludeMisappliedNames){
627
                taxa.addAll(subMisappliedNames.list());
628
            }
629

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

    
654
                    }
655
                    hql +=  " from %s t " +
656
                            " where t.id in (:synonyms) ";
657

    
658
                } else if (taxa.size()>0 ){
659
                    hql = "select " + selectWhat;
660
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
661
                    // also return the computed isOrphaned flag
662
                    if (doNotReturnFullEntities &&  !doCount ){
663
                        hql += ", 'taxon', " +
664
                                " case when t.taxonNodes is empty and t.relationsFromThisTaxon is empty and t.relationsToThisTaxon is empty then true else false end ";
665
                    }
666
                    hql +=  " from %s t " +
667
                            " where t.id in (:taxa) ";
668

    
669
                } else{
670
                    hql = "select " + selectWhat + " from %s t";
671
                }
672
            } else if(doTaxa){
673
                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){
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(doSynonyms){
688
                if (synonyms.size()>0){
689

    
690
                    hql = "select " + selectWhat;
691
                    // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
692
                    // also return the computed isOrphaned flag
693
                    if (doNotReturnFullEntities){
694
                        hql += ", 'synonym', 'false' ";
695
                    }
696
                    hql +=  " from %s t " +
697
                            " where t.id in (:synonyms) ";
698
                }else{
699
                    hql = "select " + selectWhat + " from %s t";
700
                }
701
            } else if (doIncludeMisappliedNames){
702
                hql = "select " + selectWhat;
703
                // in doNotReturnFullEntities mode it is nesscary to also return the type of the matching entities:
704
                // also return the computed isOrphaned flag
705
                if (doNotReturnFullEntities){
706
                    hql += ", 'taxon', " +
707
                            " case when t.taxonNodes is empty and t.relationsFromThisTaxon is empty and t.relationsToThisTaxon is empty then true else false end ";
708
                }
709
                hql +=  " from %s t " +
710
                        " where t.id in (:taxa) ";
711

    
712
            }
713

    
714
            String classString;
715
            if (doTaxa && doSynonyms){
716
                classString = "TaxonBase";
717
            } else if (doTaxa){
718
                classString = "Taxon";
719
            } else if (doSynonyms){
720
                classString = "Synonym";
721
            } else{//only misappliedNames
722
                classString = "Taxon";
723
            }
724

    
725
            hql = String.format(hql, classString);
726

    
727

    
728
            if (hql == "") {
729
                return null;
730
            }
731
            if(!doCount){
732
                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";
733
            }
734

    
735
            logger.debug("hql: " + hql);
736
            Query query = getSession().createQuery(hql);
737

    
738

    
739
            if (doTaxa && doSynonyms){
740
                // find taxa and synonyms
741
                if (taxa.size()>0){
742
                    query.setParameterList("taxa", taxa);
743
                }
744
                if (synonyms.size()>0){
745
                    query.setParameterList("synonyms",synonyms);
746
                }
747
                if (taxa.size()== 0 && synonyms.size() == 0){
748
                    return null;
749
                }
750
            }else if(doTaxa){
751
                //find taxa
752
                if (taxa.size()>0){
753
                    query.setParameterList("taxa", taxa );
754
                }else{
755
                    logger.warn("there are no taxa for the query: " + queryString);
756
                    return null;
757
                }
758
            } else if(doSynonyms){
759
                // find synonyms
760
                if (synonyms.size()>0){
761
                    query.setParameterList("synonyms", synonyms);
762
                }else{
763
                    return null;
764
                }
765
            }	else{
766
                //only misappliedNames
767
                if (taxa.size()>0){
768
                    query.setParameterList("taxa", taxa );
769
                }else{
770
                    return null;
771
                }
772
            }
773

    
774

    
775
            return query;
776

    
777

    
778
    }
779

    
780

    
781
    /**
782
     * @param searchField the field in TaxonNameBase to be searched through usually either <code>nameCache</code> or <code>titleCache</code>
783
     * @param queryString
784
     * @param classification TODO
785
     * @param matchMode
786
     * @param namedAreas
787
     * @param pageSize
788
     * @param pageNumber
789
     * @param doCount
790
     * @param clazz
791
     * @return
792
     *
793
     * FIXME implement classification restriction & implement test: see {@link TaxonDaoHibernateImplTest#testCountTaxaByName()}
794
     */
795
    private Query prepareTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, String searchField, String queryString,
796
            Classification classification, MatchMode matchMode, Set<NamedArea> namedAreas, Integer pageSize, Integer pageNumber, boolean doCount) {
797

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

    
800
        if(pageSize != null &&  !doCount && query != null) {
801
            query.setMaxResults(pageSize);
802
            if(pageNumber != null) {
803
                query.setFirstResult(pageNumber * pageSize);
804
            }
805
        }
806

    
807
        return query;
808
    }
809

    
810
    private Query prepareTaxaByCommonName(String queryString, Classification classification,
811
            MatchMode matchMode, Set<NamedArea> namedAreas, Integer pageSize, Integer pageNumber, boolean doCount){
812

    
813
        String what;
814
        if(doCount){
815
            what = "select  count(t)";
816
        } else {
817
            what = "select  t";
818
        }
819
        String hql= what + " from Taxon t " +
820
        "join t.descriptions d "+
821
        "join d.descriptionElements e " +
822
        "join e.feature f " +
823
        "where f.supportsCommonTaxonName = true and e.name "+matchMode.getMatchOperator()+" :queryString";//and ls.text like 'common%'";
824

    
825
        Query query = getSession().createQuery(hql);
826

    
827
        query.setParameter("queryString", queryString);
828

    
829
        if(pageSize != null &&  !doCount) {
830
            query.setMaxResults(pageSize);
831
            if(pageNumber != null) {
832
                query.setFirstResult(pageNumber * pageSize);
833
            }
834
        }
835
        return query;
836
    }
837

    
838
    @Override
839
    public long countTaxaByName(boolean doTaxa, boolean doSynonyms, boolean doMisappliedNames, String queryString, Classification classification,
840
        MatchMode matchMode, Set<NamedArea> namedAreas) {
841

    
842
        boolean doCount = true;
843
        /*
844
        boolean doTaxa = true;
845
        boolean doSynonyms = true;
846
        if (clazz.equals(Taxon.class)){
847
            doSynonyms = false;
848
        } else if (clazz.equals(Synonym.class)){
849
            doTaxa = false;
850
        }
851
        */
852

    
853

    
854
        Query query = prepareTaxaByName(doTaxa, doSynonyms, doMisappliedNames, "nameCache", queryString, classification, matchMode, namedAreas, null, null, doCount);
855
        if (query != null) {
856
            return (Long)query.uniqueResult();
857
        }else{
858
            return 0;
859
        }
860
    }
861

    
862
    /**
863
     * @param namedAreas
864
     * @param areasExpanded
865
     * @param areaQuery
866
     */
867
    private void expandNamedAreas(Collection<NamedArea> namedAreas, Set<NamedArea> areasExpanded, Query areaQuery) {
868
        List<NamedArea> childAreas;
869
        for(NamedArea a : namedAreas){
870
            areasExpanded.add(a);
871
            areaQuery.setParameter("area", a);
872
            childAreas = areaQuery.list();
873
            if(childAreas.size() > 0){
874
                areasExpanded.addAll(childAreas);
875
                expandNamedAreas(childAreas, areasExpanded, areaQuery);
876
            }
877
        }
878
    }
879

    
880
//	/* (non-Javadoc)
881
//	 * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#countTaxaByName(java.lang.String, eu.etaxonomy.cdm.persistence.query.MatchMode, eu.etaxonomy.cdm.persistence.query.SelectMode)
882
//	 */
883
//	public Integer countTaxaByName(String queryString, MatchMode matchMode, SelectMode selectMode) {
884
//		return countTaxaByName(queryString, matchMode, selectMode, null);
885
//	}
886

    
887
//	/* (non-Javadoc)
888
//	 * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#countTaxaByName(java.lang.String, eu.etaxonomy.cdm.persistence.query.MatchMode, eu.etaxonomy.cdm.persistence.query.SelectMode, eu.etaxonomy.cdm.model.reference.Reference)
889
//	 */
890
//	public Integer countTaxaByName(String queryString,
891
//			MatchMode matchMode, SelectMode selectMode, Reference sec) {
892
//
893
//		Long count = countTaxaByName(queryString, matchMode, selectMode, sec, null);
894
//		return count.intValue();
895
//
896
//	}
897

    
898
//	public Integer countTaxaByName(String queryString, MatchMode matchMode, Boolean accepted) {
899
//
900
//		SelectMode selectMode = (accepted ? SelectMode.TAXA : SelectMode.SYNONYMS);
901
//		Long count = countTaxaByName(queryString, matchMode, selectMode, null, null);
902
//		return count.intValue();
903
//	}
904

    
905
    @Override
906
    public List<TaxonBase> getAllTaxonBases(Integer pagesize, Integer page) {
907
        return super.list(pagesize, page);
908
    }
909

    
910
    @Override
911
    public List<Synonym> getAllSynonyms(Integer limit, Integer start) {
912
        Criteria criteria = getSession().createCriteria(Synonym.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<Taxon> getAllTaxa(Integer limit, Integer start) {
924
        Criteria criteria = getSession().createCriteria(Taxon.class);
925

    
926
        if(limit != null) {
927
            criteria.setFirstResult(start);
928
            criteria.setMaxResults(limit);
929
        }
930

    
931
        return criteria.list();
932
    }
933

    
934
    @Override
935
    public List<RelationshipBase> getAllRelationships(/*Class<? extends RelationshipBase> clazz,*/ Integer limit, Integer start) {
936
        Class<? extends RelationshipBase> clazz = RelationshipBase.class;  //preliminary, see #2653
937
        AuditEvent auditEvent = getAuditEventFromContext();
938
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
939
            // for some reason the HQL .class discriminator didn't work here so I created this preliminary
940
            // implementation for now. Should be cleaned in future.
941

    
942
            List<RelationshipBase> result = new ArrayList<RelationshipBase>();
943

    
944
            int taxRelSize = countAllRelationships(TaxonRelationship.class);
945

    
946
            if (taxRelSize > start){
947

    
948
                String hql = " FROM TaxonRelationship as rb ORDER BY rb.id ";
949
                Query query = getSession().createQuery(hql);
950
                query.setFirstResult(start);
951
                if (limit != null){
952
                    query.setMaxResults(limit);
953
                }
954
                result = query.list();
955
            }
956
            limit = limit - result.size();
957
            if (limit > 0){
958
                String hql = " FROM SynonymRelationship as rb ORDER BY rb.id ";
959
                Query query = getSession().createQuery(hql);
960
                start = (taxRelSize > start) ? 0 : (start - taxRelSize);
961
                query.setFirstResult(start);
962
                if (limit != null){
963
                    query.setMaxResults(limit);
964
                }
965
                result.addAll(query.list());
966
            }
967
            return result;
968

    
969
        } else {
970
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(clazz,auditEvent.getRevisionNumber());
971
            return query.getResultList();
972
        }
973
    }
974

    
975
    @Override
976
    public UUID delete(TaxonBase taxonBase) throws DataAccessException{
977
        if (taxonBase == null){
978
            logger.warn("TaxonBase was 'null'");
979
            return null;
980
        }
981

    
982
        // Merge the object in if it is detached
983
        //
984
        // I think this is preferable to catching lazy initialization errors
985
        // as that solution only swallows and hides the exception, but doesn't
986
        // actually solve it.
987
        getSession().merge(taxonBase);
988
        
989
        taxonBase.removeSources();
990
        
991
        if (taxonBase instanceof Taxon){ //	is Taxon
992
            for (Iterator<TaxonRelationship> iterator = ((Taxon)taxonBase).getRelationsFromThisTaxon().iterator(); iterator.hasNext();){
993
                TaxonRelationship relationFromThisTaxon = iterator.next();
994

    
995
                // decrease children count of taxonomic parent by one
996
                if (relationFromThisTaxon.getType().equals(TaxonRelationshipType.TAXONOMICALLY_INCLUDED_IN())) {
997
                    Taxon toTaxon = relationFromThisTaxon.getToTaxon(); // parent
998
                    if (toTaxon != null) {
999
                        toTaxon.setTaxonomicChildrenCount(toTaxon.getTaxonomicChildrenCount() - 1);
1000
                    }
1001
                }
1002
            }
1003
        }
1004

    
1005
        return super.delete(taxonBase);
1006
    }
1007

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

    
1011
        boolean doCount = false;
1012
        Query query = prepareTaxaByName(doTaxa, doSynonyms, false, "titleCache", queryString, classification, matchMode, namedAreas, pageSize, pageNumber, doCount);
1013
        if (query != null){
1014
            List<TaxonBase> results = query.list();
1015
            defaultBeanInitializer.initializeAll(results, propertyPaths);
1016
            return results;
1017
        }
1018
        return new ArrayList<TaxonBase>();
1019

    
1020
    }
1021

    
1022
    @Override
1023
    public TaxonBase findByUuid(UUID uuid, List<Criterion> criteria, List<String> propertyPaths) {
1024

    
1025
        Criteria crit = getSession().createCriteria(type);
1026

    
1027
        if (uuid != null) {
1028
            crit.add(Restrictions.eq("uuid", uuid));
1029
        } else {
1030
            logger.warn("UUID is NULL");
1031
            return null;
1032
        }
1033
        if(criteria != null){
1034
            for (Criterion criterion : criteria) {
1035
                crit.add(criterion);
1036
            }
1037
        }
1038
        crit.addOrder(Order.asc("uuid"));
1039

    
1040
        List<? extends TaxonBase> results = crit.list();
1041
        if (results.size() == 1) {
1042
            defaultBeanInitializer.initializeAll(results, propertyPaths);
1043
            TaxonBase taxon = results.iterator().next();
1044
            return taxon;
1045
        } else if (results.size() > 1) {
1046
            logger.error("Multiple results for UUID: " + uuid);
1047
        } else if (results.size() == 0) {
1048
            logger.info("No results for UUID: " + uuid);
1049
        }
1050

    
1051
        return null;
1052
    }
1053

    
1054
    @Override
1055
    public List<? extends TaxonBase> findByUuids(List<UUID> uuids, List<Criterion> criteria, List<String> propertyPaths) {
1056

    
1057
        Criteria crit = getSession().createCriteria(type);
1058

    
1059
        if (uuids != null) {
1060
            crit.add(Restrictions.in("uuid", uuids));
1061
        } else {
1062
            logger.warn("List<UUID> uuids is NULL");
1063
            return null;
1064
        }
1065
        if(criteria != null){
1066
            for (Criterion criterion : criteria) {
1067
                crit.add(criterion);
1068
            }
1069
        }
1070
        crit.addOrder(Order.asc("uuid"));
1071

    
1072
        List<? extends TaxonBase> results = crit.list();
1073

    
1074
        defaultBeanInitializer.initializeAll(results, propertyPaths);
1075
        return results;
1076
    }
1077

    
1078
    @Override
1079
    public int countMatchesByName(String queryString, MatchMode matchMode, boolean onlyAcccepted) {
1080
        checkNotInPriorView("TaxonDaoHibernateImpl.countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted)");
1081
        Criteria crit = getSession().createCriteria(type);
1082
        crit.add(Restrictions.ilike("titleCache", matchMode.queryStringFrom(queryString)));
1083
        crit.setProjection(Projections.rowCount());
1084
        int result = ((Number)crit.list().get(0)).intValue();
1085
        return result;
1086
    }
1087

    
1088

    
1089
    @Override
1090
    public int countMatchesByName(String queryString, MatchMode matchMode, boolean onlyAcccepted, List<Criterion> criteria) {
1091
        checkNotInPriorView("TaxonDaoHibernateImpl.countMatchesByName(String queryString, ITitledDao.MATCH_MODE matchMode, boolean onlyAcccepted, List<Criterion> criteria)");
1092
        Criteria crit = getSession().createCriteria(type);
1093
        crit.add(Restrictions.ilike("titleCache", matchMode.queryStringFrom(queryString)));
1094
        if(criteria != null){
1095
            for (Criterion criterion : criteria) {
1096
                crit.add(criterion);
1097
            }
1098
        }
1099
        crit.setProjection(Projections.rowCount());
1100
        int result = ((Number)crit.list().get(0)).intValue();
1101
        return result;
1102
    }
1103

    
1104
    @Override
1105
    public int countTaxonRelationships(Taxon taxon, TaxonRelationshipType type, Direction direction) {
1106
        AuditEvent auditEvent = getAuditEventFromContext();
1107
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1108
            Query query = null;
1109

    
1110
            if(type == null) {
1111
                query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon");
1112
            } else {
1113
                query = getSession().createQuery("select count(taxonRelationship) from TaxonRelationship taxonRelationship where taxonRelationship."+direction+" = :relatedTaxon and taxonRelationship.type = :type");
1114
                query.setParameter("type",type);
1115
            }
1116
            query.setParameter("relatedTaxon", taxon);
1117

    
1118
            return ((Long)query.uniqueResult()).intValue();
1119
        } else {
1120
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1121
            query.add(AuditEntity.relatedId(direction.toString()).eq(taxon.getId()));
1122
            query.addProjection(AuditEntity.id().count("id"));
1123

    
1124
            if(type != null) {
1125
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1126
            }
1127

    
1128
            return ((Long)query.getSingleResult()).intValue();
1129
        }
1130
    }
1131

    
1132
    @Override
1133
    public int countSynonyms(Taxon taxon, SynonymRelationshipType type) {
1134
        AuditEvent auditEvent = getAuditEventFromContext();
1135
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1136
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1137

    
1138
            criteria.add(Restrictions.eq("relatedTo", taxon));
1139
            if(type != null) {
1140
                criteria.add(Restrictions.eq("type", type));
1141
            }
1142
            criteria.setProjection(Projections.rowCount());
1143
            return ((Number)criteria.uniqueResult()).intValue();
1144
        } else {
1145
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());
1146
            query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));
1147
            query.addProjection(AuditEntity.id().count("id"));
1148

    
1149
            if(type != null) {
1150
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1151
            }
1152

    
1153
            return ((Long)query.getSingleResult()).intValue();
1154
        }
1155
    }
1156

    
1157
    @Override
1158
    public int countSynonyms(Synonym synonym, SynonymRelationshipType type) {
1159
        AuditEvent auditEvent = getAuditEventFromContext();
1160
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1161
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1162

    
1163
            criteria.add(Restrictions.eq("relatedFrom", synonym));
1164
            if(type != null) {
1165
                criteria.add(Restrictions.eq("type", type));
1166
            }
1167

    
1168
            criteria.setProjection(Projections.rowCount());
1169
            return ((Number)criteria.uniqueResult()).intValue();
1170
        } else {
1171
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());
1172
            query.add(AuditEntity.relatedId("relatedFrom").eq(synonym.getId()));
1173
            query.addProjection(AuditEntity.id().count("id"));
1174

    
1175
            if(type != null) {
1176
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1177
            }
1178

    
1179
            return ((Long)query.getSingleResult()).intValue();
1180
        }
1181
    }
1182

    
1183
    @Override
1184
    public int countTaxaByName(Class<? extends TaxonBase> clazz, String genusOrUninomial, String infraGenericEpithet, String specificEpithet,	String infraSpecificEpithet, Rank rank) {
1185
        checkNotInPriorView("TaxonDaoHibernateImpl.countTaxaByName(Boolean accepted, String genusOrUninomial,	String infraGenericEpithet, String specificEpithet,	String infraSpecificEpithet, Rank rank)");
1186
        Criteria criteria = null;
1187

    
1188
        criteria = getSession().createCriteria(clazz);
1189

    
1190
        criteria.setFetchMode( "name", FetchMode.JOIN );
1191
        criteria.createAlias("name", "name");
1192

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

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

    
1205
        if(specificEpithet == null) {
1206
            criteria.add(Restrictions.isNull("name.specificEpithet"));
1207
        } else if(!specificEpithet.equals("*")) {
1208
            criteria.add(Restrictions.eq("name.specificEpithet", specificEpithet));
1209

    
1210
        }
1211

    
1212
        if(infraSpecificEpithet == null) {
1213
            criteria.add(Restrictions.isNull("name.infraSpecificEpithet"));
1214
        } else if(!infraSpecificEpithet.equals("*")) {
1215
            criteria.add(Restrictions.eq("name.infraSpecificEpithet", infraSpecificEpithet));
1216
        }
1217

    
1218
        if(rank != null) {
1219
            criteria.add(Restrictions.eq("name.rank", rank));
1220
        }
1221

    
1222
        criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
1223

    
1224
        return ((Number)criteria.uniqueResult()).intValue();
1225
    }
1226

    
1227
    @Override
1228
    public List<TaxonBase> findTaxaByName(Class<? extends TaxonBase> clazz, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, Rank rank, Integer pageSize,	Integer pageNumber) {
1229
        checkNotInPriorView("TaxonDaoHibernateImpl.findTaxaByName(Boolean accepted, String genusOrUninomial, String infraGenericEpithet, String specificEpithet, String infraSpecificEpithet, Rank rank, Integer pageSize,	Integer pageNumber)");
1230
        Criteria criteria = null;
1231
        if (clazz == null){
1232
            criteria = getSession().createCriteria(TaxonBase.class);
1233
        } else{
1234
            criteria = getSession().createCriteria(clazz);
1235
        }
1236
        criteria.setFetchMode( "name", FetchMode.JOIN );
1237
        criteria.createAlias("name", "name");
1238

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

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

    
1251
        if(specificEpithet == null) {
1252
            criteria.add(Restrictions.isNull("name.specificEpithet"));
1253
        } else if(!specificEpithet.equals("*")) {
1254
            criteria.add(Restrictions.eq("name.specificEpithet", specificEpithet));
1255

    
1256
        }
1257

    
1258
        if(infraSpecificEpithet == null) {
1259
            criteria.add(Restrictions.isNull("name.infraSpecificEpithet"));
1260
        } else if(!infraSpecificEpithet.equals("*")) {
1261
            criteria.add(Restrictions.eq("name.infraSpecificEpithet", infraSpecificEpithet));
1262
        }
1263

    
1264
        if(rank != null) {
1265
            criteria.add(Restrictions.eq("name.rank", rank));
1266
        }
1267

    
1268
        if(pageSize != null) {
1269
            criteria.setMaxResults(pageSize);
1270
            if(pageNumber != null) {
1271
                criteria.setFirstResult(pageNumber * pageSize);
1272
            } else {
1273
                criteria.setFirstResult(0);
1274
            }
1275
        }
1276

    
1277
        return criteria.list();
1278
    }
1279

    
1280
    @Override
1281
    public List<TaxonRelationship> getTaxonRelationships(Taxon taxon, TaxonRelationshipType type,
1282
            Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths, Direction direction) {
1283

    
1284
        AuditEvent auditEvent = getAuditEventFromContext();
1285
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1286

    
1287
            Criteria criteria = getSession().createCriteria(TaxonRelationship.class);
1288

    
1289
            if(direction != null) {
1290
                criteria.add(Restrictions.eq(direction.name(), taxon));
1291
            } else {
1292
                criteria.add(Restrictions.or(
1293
                        Restrictions.eq(Direction.relatedFrom.name(), taxon),
1294
                        Restrictions.eq(Direction.relatedTo.name(), taxon))
1295
                    );
1296
            }
1297

    
1298
            if(type != null) {
1299
                criteria.add(Restrictions.eq("type", type));
1300
            }
1301

    
1302
            addOrder(criteria,orderHints);
1303

    
1304
            if(pageSize != null) {
1305
                criteria.setMaxResults(pageSize);
1306
                if(pageNumber != null) {
1307
                    criteria.setFirstResult(pageNumber * pageSize);
1308
                } else {
1309
                    criteria.setFirstResult(0);
1310
                }
1311
            }
1312

    
1313
            List<TaxonRelationship> result = criteria.list();
1314
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1315

    
1316
            return result;
1317
        } else {
1318
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1319
            query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));
1320

    
1321
            if(type != null) {
1322
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1323
            }
1324

    
1325
            if(pageSize != null) {
1326
                query.setMaxResults(pageSize);
1327
                if(pageNumber != null) {
1328
                    query.setFirstResult(pageNumber * pageSize);
1329
                } else {
1330
                    query.setFirstResult(0);
1331
                }
1332
            }
1333

    
1334
            List<TaxonRelationship> result = query.getResultList();
1335
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1336

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

    
1346
            return result;
1347
        }
1348
    }
1349

    
1350
    class TaxonRelationshipFromTaxonComparator implements Comparator<TaxonRelationship> {
1351

    
1352
        @Override
1353
        public int compare(TaxonRelationship o1, TaxonRelationship o2) {
1354
            return o1.getFromTaxon().getTitleCache().compareTo(o2.getFromTaxon().getTitleCache());
1355
        }
1356

    
1357
    }
1358

    
1359
    class SynonymRelationshipFromTaxonComparator implements Comparator<SynonymRelationship> {
1360

    
1361
        @Override
1362
        public int compare(SynonymRelationship o1, SynonymRelationship o2) {
1363
            return o1.getSynonym().getTitleCache().compareTo(o2.getSynonym().getTitleCache());
1364
        }
1365

    
1366
    }
1367

    
1368
    @Override
1369
    public List<SynonymRelationship> getSynonyms(Taxon taxon, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
1370
        AuditEvent auditEvent = getAuditEventFromContext();
1371
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1372
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1373

    
1374
            criteria.add(Restrictions.eq("relatedTo", taxon));
1375
            if(type != null) {
1376
                criteria.add(Restrictions.eq("type", type));
1377
            }
1378

    
1379
            addOrder(criteria,orderHints);
1380

    
1381
            if(pageSize != null) {
1382
                criteria.setMaxResults(pageSize);
1383
                if(pageNumber != null) {
1384
                    criteria.setFirstResult(pageNumber * pageSize);
1385
                } else {
1386
                    criteria.setFirstResult(0);
1387
                }
1388
            }
1389

    
1390
            List<SynonymRelationship> result = criteria.list();
1391
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1392

    
1393
            return result;
1394
        } else {
1395
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());
1396
            query.add(AuditEntity.relatedId("relatedTo").eq(taxon.getId()));
1397

    
1398
            if(type != null) {
1399
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1400
            }
1401

    
1402
            if(pageSize != null) {
1403
                query.setMaxResults(pageSize);
1404
                if(pageNumber != null) {
1405
                    query.setFirstResult(pageNumber * pageSize);
1406
                } else {
1407
                    query.setFirstResult(0);
1408
                }
1409
            }
1410

    
1411
            List<SynonymRelationship> result = query.getResultList();
1412
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1413

    
1414
            return result;
1415
        }
1416
    }
1417

    
1418
    @Override
1419
    public List<SynonymRelationship> getSynonyms(Synonym synonym, SynonymRelationshipType type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
1420
        AuditEvent auditEvent = getAuditEventFromContext();
1421
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1422
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1423

    
1424
            criteria.add(Restrictions.eq("relatedFrom", synonym));
1425
            if(type != null) {
1426
                criteria.add(Restrictions.eq("type", type));
1427
            }
1428

    
1429
            addOrder(criteria,orderHints);
1430

    
1431
            if(pageSize != null) {
1432
                criteria.setMaxResults(pageSize);
1433
                if(pageNumber != null) {
1434
                    criteria.setFirstResult(pageNumber * pageSize);
1435
                } else {
1436
                    criteria.setFirstResult(0);
1437
                }
1438
            }
1439

    
1440
            List<SynonymRelationship> result = criteria.list();
1441
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1442

    
1443
            return result;
1444
        } else {
1445
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(SynonymRelationship.class,auditEvent.getRevisionNumber());
1446
            query.add(AuditEntity.relatedId("relatedFrom").eq(synonym.getId()));
1447

    
1448
            if(type != null) {
1449
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1450
            }
1451

    
1452
            if(pageSize != null) {
1453
                query.setMaxResults(pageSize);
1454
                if(pageNumber != null) {
1455
                    query.setFirstResult(pageNumber * pageSize);
1456
                } else {
1457
                    query.setFirstResult(0);
1458
                }
1459
            }
1460

    
1461
            List<SynonymRelationship> result = query.getResultList();
1462
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1463

    
1464
            return result;
1465
        }
1466
    }
1467

    
1468
    @Override
1469
    public void rebuildIndex() {
1470
        FullTextSession fullTextSession = Search.getFullTextSession(getSession());
1471

    
1472
        for(TaxonBase taxonBase : list(null,null)) { // re-index all taxon base
1473
            Hibernate.initialize(taxonBase.getName());
1474
            fullTextSession.index(taxonBase);
1475
        }
1476
        fullTextSession.flushToIndexes();
1477
    }
1478

    
1479
    @Override
1480
    public String suggestQuery(String queryString) {
1481
        checkNotInPriorView("TaxonDaoHibernateImpl.suggestQuery(String queryString)");
1482
        String alternativeQueryString = null;
1483
        if (alternativeSpellingSuggestionParser != null) {
1484
            try {
1485

    
1486
                alternativeSpellingSuggestionParser.parse(queryString);
1487
                org.apache.lucene.search.Query alternativeQuery = alternativeSpellingSuggestionParser.suggest(queryString);
1488
                if (alternativeQuery != null) {
1489
                    alternativeQueryString = alternativeQuery
1490
                            .toString("name.titleCache");
1491
                }
1492

    
1493
            } catch (ParseException e) {
1494
                throw new QueryParseException(e, queryString);
1495
            }
1496
        }
1497
        return alternativeQueryString;
1498
    }
1499

    
1500
    @Override
1501
    public List<Taxon> listAcceptedTaxaFor(Synonym synonym, Classification classificationFilter, Integer pageSize, Integer pageNumber,
1502
            List<OrderHint> orderHints, List<String> propertyPaths){
1503

    
1504
        String hql = prepareListAcceptedTaxaFor(classificationFilter, orderHints, false);
1505

    
1506
        Query query = getSession().createQuery(hql);
1507

    
1508
        query.setParameter("synonym", synonym);
1509

    
1510
        if(classificationFilter != null){
1511
            query.setParameter("classificationFilter", classificationFilter);
1512
        }
1513

    
1514

    
1515
        if(pageSize != null) {
1516
            query.setMaxResults(pageSize);
1517
            if(pageNumber != null) {
1518
                query.setFirstResult(pageNumber * pageSize);
1519
            }
1520
        }
1521

    
1522
        @SuppressWarnings("unchecked")
1523
        List<Taxon> result = query.list();
1524

    
1525
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1526

    
1527
        return result;
1528

    
1529
    }
1530

    
1531
    @Override
1532
    public long countAcceptedTaxaFor(Synonym synonym, Classification classificationFilter){
1533

    
1534
        String hql = prepareListAcceptedTaxaFor(classificationFilter, null, true);
1535

    
1536
        Query query = getSession().createQuery(hql);
1537

    
1538
        query.setParameter("synonym", synonym);
1539

    
1540
        if(classificationFilter != null){
1541
            query.setParameter("classificationFilter", classificationFilter);
1542
        }
1543

    
1544
        Long count = Long.parseLong(query.uniqueResult().toString());
1545

    
1546
        return count;
1547

    
1548
    }
1549

    
1550

    
1551
    /**
1552
     * @param classificationFilter
1553
     * @param orderHints
1554
     * @return
1555
     */
1556
    private String prepareListAcceptedTaxaFor(Classification classificationFilter, List<OrderHint> orderHints, boolean doCount) {
1557

    
1558
        String hql;
1559
        String hqlSelect = "select " + (doCount? "count(taxon)" : "taxon") + " from Taxon as taxon left join taxon.synonymRelations as synRel ";
1560
        String hqlWhere = " where synRel.relatedFrom = :synonym";
1561

    
1562
        if(classificationFilter != null){
1563
            hqlSelect += " left join taxon.taxonNodes AS taxonNode";
1564
            hqlWhere += " and taxonNode.classification = :classificationFilter";
1565
        }
1566
        hql = hqlSelect + hqlWhere + orderByClause(orderHints, "taxon");
1567
        return hql;
1568
    }
1569

    
1570
    @Override
1571
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification) {
1572

    
1573
        int classificationId = classification.getId();
1574

    
1575
        String queryString = "SELECT nodes.uuid, taxa.titleCache FROM TaxonNode AS nodes LEFT JOIN TaxonBase AS taxa ON nodes.taxon_id = taxa.id WHERE taxa.DTYPE = 'Taxon' AND nodes.classification_id = " + classificationId;
1576

    
1577
        List<Object[]> result = getSession().createSQLQuery(queryString).list();
1578

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

    
1584
            for (Object object : result){
1585

    
1586
                Object[] objectArray = (Object[]) object;
1587

    
1588
                UUID uuid = UUID.fromString((String) objectArray[0]);
1589
                String titleCache = (String) objectArray[1];
1590

    
1591
                list.add(new UuidAndTitleCache(TaxonNode.class, uuid, titleCache));
1592
            }
1593

    
1594
            return list;
1595
        }
1596
    }
1597

    
1598

    
1599
    public class UuidAndTitleCacheOfAcceptedTaxon{
1600
        UUID uuid;
1601

    
1602
        String titleCache;
1603

    
1604
        public UuidAndTitleCacheOfAcceptedTaxon(UUID uuid, String titleCache){
1605
            this.uuid = uuid;
1606
            this.titleCache = titleCache;
1607
        }
1608

    
1609
        public UUID getUuid() {
1610
            return uuid;
1611
        }
1612

    
1613
        public void setUuid(UUID uuid) {
1614
            this.uuid = uuid;
1615
        }
1616

    
1617
        public String getTitleCache() {
1618
            return titleCache;
1619
        }
1620

    
1621
        public void setTitleCache(String titleCache) {
1622
            this.titleCache = titleCache;
1623
        }
1624
    }
1625

    
1626
    @Override
1627
    public TaxonBase find(LSID lsid) {
1628
        TaxonBase<?> taxonBase = super.find(lsid);
1629
        if(taxonBase != null) {
1630
            List<String> propertyPaths = new ArrayList<String>();
1631
            propertyPaths.add("createdBy");
1632
            propertyPaths.add("updatedBy");
1633
            propertyPaths.add("name");
1634
            propertyPaths.add("sec");
1635
            propertyPaths.add("relationsToThisTaxon");
1636
            propertyPaths.add("relationsToThisTaxon.fromTaxon");
1637
            propertyPaths.add("relationsToThisTaxon.toTaxon");
1638
            propertyPaths.add("relationsFromThisTaxon");
1639
            propertyPaths.add("relationsFromThisTaxon.toTaxon");
1640
            propertyPaths.add("relationsToThisTaxon.type");
1641
            propertyPaths.add("synonymRelations");
1642
            propertyPaths.add("synonymRelations.synonym");
1643
            propertyPaths.add("synonymRelations.type");
1644
            propertyPaths.add("descriptions");
1645

    
1646
            defaultBeanInitializer.initialize(taxonBase, propertyPaths);
1647
        }
1648
        return taxonBase;
1649
    }
1650

    
1651
    public List<TaxonBase> getTaxaByCommonName(String queryString,
1652
            Classification classification, MatchMode matchMode,
1653
            Set<NamedArea> namedAreas, Integer pageSize, Integer pageNumber) {
1654
        logger.warn("getTaxaByCommonName not yet implemented.");
1655
        return null;
1656
    }
1657

    
1658

    
1659

    
1660

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

    
1663
        for (SynonymRelationship synonymRelation:synonymRelationships){
1664
            TaxonNameBase synName;
1665
            NonViralName inferredSynName;
1666
            Synonym syn = synonymRelation.getSynonym();
1667
            HibernateProxyHelper.deproxy(syn);
1668

    
1669
            synName = syn.getName();
1670
            ZoologicalName zooName = zooHashMap.get(synName.getUuid());
1671
            String synGenusName = zooName.getGenusOrUninomial();
1672

    
1673
            switch(type.getId()){
1674
            case SynonymRelationshipType.INFERRED_EPITHET_OF().getId():
1675
                inferredSynName.setSpecificEpithet(addString);
1676
                break;
1677
            case SynonymRelationshipType.INFERRED_GENUS_OF().getId():
1678
                break;
1679
            case SynonymRelationshipType.POTENTIAL_COMBINATION_OF().getId():
1680
                break;
1681
            default:
1682
            }
1683
            if (!synonymsGenus.contains(synGenusName)){
1684
                synonymsGenus.add(synGenusName);
1685
            }
1686
            inferredSynName = NonViralName.NewInstance(Rank.SPECIES());
1687
            inferredSynName.setSpecificEpithet(epithetOfTaxon);
1688
            inferredSynName.setGenusOrUninomial(synGenusName);
1689
            inferredEpithet = Synonym.NewInstance(inferredSynName, null);
1690
            taxon.addSynonym(inferredEpithet, SynonymRelationshipType.INFERRED_GENUS_OF());
1691
            inferredSynonyms.add(inferredEpithet);
1692
            inferredSynName.generateTitle();
1693
            taxonNames.add(inferredSynName.getNameCache());
1694
        }
1695

    
1696

    
1697
        if (!taxonNames.isEmpty()){
1698
        List<String> synNotInCDM = this.taxaByNameNotInDB(taxonNames);
1699
        ZoologicalName name;
1700
        if (!synNotInCDM.isEmpty()){
1701
            for (Synonym syn :inferredSynonyms){
1702
                name =zooHashMap.get(syn.getName().getUuid());
1703
                if (!synNotInCDM.contains(name.getNameCache())){
1704
                    inferredSynonyms.remove(syn);
1705
                }
1706
            }
1707
        }
1708
        }
1709
    }*/
1710

    
1711
    @Override
1712
    public int countAllRelationships() {
1713
        return countAllRelationships(null);
1714
    }
1715

    
1716
    /*
1717
     * (non-Javadoc)
1718
     * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#countAllRelationships()
1719
     */
1720
    //FIXME add to interface or make private
1721
    public int countAllRelationships(Class<? extends RelationshipBase> clazz) {
1722
        if (clazz != null && ! TaxonRelationship.class.isAssignableFrom(clazz) && ! SynonymRelationship.class.isAssignableFrom(clazz) ){
1723
            throw new RuntimeException("Class must be assignable by a taxon or snonym relation");
1724
        }
1725
        int size = 0;
1726

    
1727
        if (clazz == null || TaxonRelationship.class.isAssignableFrom(clazz)){
1728
            String hql = " SELECT count(rel) FROM TaxonRelationship rel";
1729
            size += (Long)getSession().createQuery(hql).list().get(0);
1730
        }
1731
        if (clazz == null || SynonymRelationship.class.isAssignableFrom(clazz)){
1732
            String hql = " SELECT count(rel) FROM SynonymRelationship rel";
1733
            size += (Long)getSession().createQuery(hql).list().get(0);
1734
        }
1735
        return size;
1736
    }
1737

    
1738
    @Override
1739
    public List<String> taxaByNameNotInDB(List<String> taxonNames){
1740
        List<TaxonBase> notInDB = new ArrayList<TaxonBase>();
1741
        //get all taxa, already in db
1742
        Query query = getSession().createQuery("from TaxonNameBase t where t.nameCache IN (:taxonList)");
1743
        query.setParameterList("taxonList", taxonNames);
1744
        List<TaxonNameBase> taxaInDB = query.list();
1745
        //compare the original list with the result of the query
1746
        for (TaxonNameBase taxonName: taxaInDB){
1747
            if (taxonName.isInstanceOf(NonViralName.class)) {
1748
                NonViralName nonViralName = CdmBase.deproxy(taxonName, NonViralName.class);
1749
                String nameCache = nonViralName.getNameCache();
1750
                if (taxonNames.contains(nameCache)){
1751
                    taxonNames.remove(nameCache);
1752
                }
1753
            }
1754
        }
1755

    
1756
        return taxonNames;
1757
    }
1758

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

    
1765
        return zooNames;
1766

    
1767
    }
1768

    
1769
    @Override
1770
    public List<TaxonNameBase> findIdenticalTaxonNames(List<String> propertyPaths) {
1771

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

    
1774
        List<TaxonNameBase> zooNames = query.list();
1775

    
1776
        TaxonNameComparator taxComp = new TaxonNameComparator();
1777
        Collections.sort(zooNames, taxComp);
1778

    
1779
        for (TaxonNameBase taxonNameBase: zooNames){
1780
            defaultBeanInitializer.initialize(taxonNameBase, propertyPaths);
1781
        }
1782

    
1783
        return zooNames;
1784
    }
1785

    
1786
    @Override
1787
    public List<TaxonNameBase> findIdenticalNamesNew(List<String> propertyPaths){
1788

    
1789
        //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)
1790
        Query query = getSession().createQuery("Select id from Reference where titleCache like 'Fauna Europaea database'");
1791
        List<String> secRefFauna = query.list();
1792
        query = getSession().createQuery("Select id from Reference where titleCache like 'ERMS'");
1793
        List<String> secRefErms = query.list();
1794
        //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");
1795
        //Get all names of fauna europaea
1796
        query = getSession().createQuery("select zn.nameCache from ZoologicalName zn, TaxonBase tb where tb.name = zn and tb.sec.id = :secRefFauna");
1797
        query.setParameter("secRefFauna", secRefFauna.get(0));
1798
        List<String> namesFauna= query.list();
1799

    
1800
        //Get all names of erms
1801

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

    
1805
        List<String> namesErms = query.list();
1806
        /*TaxonNameComparator comp = new TaxonNameComparator();
1807
        Collections.sort(namesFauna);
1808
        Collections.sort(namesErms);
1809
        */
1810
        List <String> identicalNames = new ArrayList<String>();
1811
        String predecessor = "";
1812

    
1813
        for (String nameFauna: namesFauna){
1814
            if (namesErms.contains(nameFauna)){
1815
                identicalNames.add(nameFauna);
1816
            }
1817
        }
1818

    
1819

    
1820
        query = getSession().createQuery("from ZoologicalName zn where zn.nameCache IN (:identicalNames)");
1821
        query.setParameterList("identicalNames", identicalNames);
1822
        List<TaxonNameBase> result = query.list();
1823
        TaxonNameBase temp = result.get(0);
1824

    
1825
        Iterator<OriginalSourceBase> sources = temp.getSources().iterator();
1826

    
1827
        TaxonNameComparator taxComp = new TaxonNameComparator();
1828
        Collections.sort(result, taxComp);
1829
        defaultBeanInitializer.initializeAll(result, propertyPaths);
1830
        return result;
1831

    
1832
    }
1833

    
1834

    
1835

    
1836
    @Override
1837
    public String getPhylumName(TaxonNameBase name){
1838
        List results = new ArrayList();
1839
        try{
1840
        Query query = getSession().createSQLQuery("select getPhylum("+ name.getId()+");");
1841
        results = query.list();
1842
        }catch(Exception e){
1843
            System.err.println(name.getUuid());
1844
            return null;
1845
        }
1846
        System.err.println("phylum of "+ name.getTitleCache() );
1847
        return (String)results.get(0);
1848
    }
1849

    
1850

    
1851
    @Override
1852
    public long countTaxaByCommonName(String searchString,
1853
            Classification classification, MatchMode matchMode,
1854
            Set<NamedArea> namedAreas) {
1855
        boolean doCount = true;
1856
        Query query = prepareTaxaByCommonName(searchString, classification, matchMode, namedAreas, null, null, doCount);
1857
        if (query != null && !query.list().isEmpty()) {
1858
            Object o = query.uniqueResult();
1859
            if(o != null) {
1860
                return (Long)o;
1861
            }
1862
        }
1863
        return 0;
1864
    }
1865

    
1866
    @Override
1867
    public long deleteSynonymRelationships(Synonym synonym, Taxon taxon) {
1868

    
1869
        String hql = "delete SynonymRelationship sr where sr.relatedFrom = :syn ";
1870
        if (taxon != null){
1871
            hql += " and sr.relatedTo = :taxon";
1872
        }
1873
        Session session = this.getSession();
1874
        Query q = session.createQuery(hql);
1875

    
1876
        q.setParameter("syn", synonym);
1877
        if (taxon != null){
1878
            q.setParameter("taxon", taxon);
1879
        }
1880
        long result = q.executeUpdate();
1881

    
1882
        return result;
1883
    }
1884

    
1885

    
1886
    @Override
1887
    public Integer countSynonymRelationships(TaxonBase taxonBase,
1888
            SynonymRelationshipType type, Direction relatedfrom) {
1889
        AuditEvent auditEvent = getAuditEventFromContext();
1890
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1891
            Query query = null;
1892

    
1893
            if(type == null) {
1894
                query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship."+relatedfrom+" = :relatedSynonym");
1895
            } else {
1896
                query = getSession().createQuery("select count(synonymRelationship) from SynonymRelationship synonymRelationship where synonymRelationship."+relatedfrom+" = :relatedSynonym and synonymRelationship.type = :type");
1897
                query.setParameter("type",type);
1898
            }
1899
            query.setParameter("relatedTaxon", taxonBase);
1900

    
1901
            return ((Long)query.uniqueResult()).intValue();
1902
        } else {
1903
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1904
            query.add(AuditEntity.relatedId(relatedfrom.toString()).eq(taxonBase.getId()));
1905
            query.addProjection(AuditEntity.id().count("id"));
1906

    
1907
            if(type != null) {
1908
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1909
            }
1910

    
1911
            return ((Long)query.getSingleResult()).intValue();
1912
        }
1913
    }
1914

    
1915

    
1916
    @Override
1917
    public List<SynonymRelationship> getSynonymRelationships(TaxonBase taxonBase,
1918
            SynonymRelationshipType type, Integer pageSize, Integer pageNumber,
1919
            List<OrderHint> orderHints, List<String> propertyPaths,
1920
            Direction direction) {
1921

    
1922
        AuditEvent auditEvent = getAuditEventFromContext();
1923
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
1924
            Criteria criteria = getSession().createCriteria(SynonymRelationship.class);
1925

    
1926
            if (direction.equals(Direction.relatedTo)){
1927
                criteria.add(Restrictions.eq("relatedTo", taxonBase));
1928
            }else{
1929
                criteria.add(Restrictions.eq("relatedFrom", taxonBase));
1930
            }
1931
            if(type != null) {
1932
                criteria.add(Restrictions.eq("type", type));
1933
            }
1934

    
1935
            addOrder(criteria,orderHints);
1936

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

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

    
1949
            return result;
1950
        } else {
1951
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonRelationship.class,auditEvent.getRevisionNumber());
1952

    
1953
            if (direction.equals(Direction.relatedTo)){
1954
                query.add(AuditEntity.relatedId("relatedTo").eq(taxonBase.getId()));
1955
            }else{
1956
                query.add(AuditEntity.relatedId("relatedFrom").eq(taxonBase.getId()));
1957
            }
1958

    
1959
            if(type != null) {
1960
                query.add(AuditEntity.relatedId("type").eq(type.getId()));
1961
            }
1962

    
1963
            if(pageSize != null) {
1964
                query.setMaxResults(pageSize);
1965
                if(pageNumber != null) {
1966
                    query.setFirstResult(pageNumber * pageSize);
1967
                } else {
1968
                    query.setFirstResult(0);
1969
                }
1970
            }
1971

    
1972
            List<SynonymRelationship> result = query.getResultList();
1973
            defaultBeanInitializer.initializeAll(result, propertyPaths);
1974

    
1975
            // Ugly, but for now, there is no way to sort on a related entity property in Envers,
1976
            // and we can't live without this functionality in CATE as it screws up the whole
1977
            // taxon tree thing
1978
            if(orderHints != null && !orderHints.isEmpty()) {
1979
                SortedSet<SynonymRelationship> sortedList = new TreeSet<SynonymRelationship>(new SynonymRelationshipFromTaxonComparator());
1980
                sortedList.addAll(result);
1981
                return new ArrayList<SynonymRelationship>(sortedList);
1982
            }
1983

    
1984
            return result;
1985
        }
1986
    }
1987

    
1988

    
1989
    /* (non-Javadoc)
1990
     * @see eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao#getUuidAndTitleCacheTaxon()
1991
     */
1992
    @Override
1993
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheTaxon() {
1994
        String queryString = String.format("select uuid, titleCache from %s where DTYPE = '%s'", type.getSimpleName(), Taxon.class.getSimpleName());
1995
        Query query = getSession().createQuery(queryString);
1996

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

    
1999
        return result;
2000
    }
2001

    
2002
    @Override
2003
    public List<UuidAndTitleCache<TaxonBase>> getUuidAndTitleCacheSynonym() {
2004
        String queryString = String.format("select uuid, titleCache from %s where DTYPE = '%s'", type.getSimpleName(), Synonym.class.getSimpleName());
2005
        Query query = getSession().createQuery(queryString);
2006

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

    
2009
        return result;
2010
    }
2011

    
2012

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2119

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

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

    
2129
        return result;
2130
    }
2131

    
2132

    
2133
}
(3-3/4)