Project

General

Profile

Download (38.2 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

    
10
package eu.etaxonomy.cdm.persistence.dao.hibernate.description;
11

    
12
import java.util.ArrayList;
13
import java.util.HashSet;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.Set;
17
import java.util.UUID;
18

    
19
import org.apache.commons.lang.ArrayUtils;
20
import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
21
import org.hibernate.Criteria;
22
import org.hibernate.criterion.Criterion;
23
import org.hibernate.criterion.ProjectionList;
24
import org.hibernate.criterion.Projections;
25
import org.hibernate.criterion.Restrictions;
26
import org.hibernate.envers.query.AuditEntity;
27
import org.hibernate.envers.query.AuditQuery;
28
import org.hibernate.query.Query;
29
import org.springframework.beans.factory.annotation.Qualifier;
30
import org.springframework.stereotype.Repository;
31

    
32
import eu.etaxonomy.cdm.model.common.LSID;
33
import eu.etaxonomy.cdm.model.common.MarkerType;
34
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
35
import eu.etaxonomy.cdm.model.description.DescriptionBase;
36
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
37
import eu.etaxonomy.cdm.model.description.DescriptionType;
38
import eu.etaxonomy.cdm.model.description.Feature;
39
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
40
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
41
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
42
import eu.etaxonomy.cdm.model.description.TaxonDescription;
43
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
44
import eu.etaxonomy.cdm.model.location.NamedArea;
45
import eu.etaxonomy.cdm.model.media.Media;
46
import eu.etaxonomy.cdm.model.name.TaxonName;
47
import eu.etaxonomy.cdm.model.taxon.Taxon;
48
import eu.etaxonomy.cdm.model.term.DefinedTerm;
49
import eu.etaxonomy.cdm.model.view.AuditEvent;
50
import eu.etaxonomy.cdm.persistence.dao.common.OperationNotSupportedInPriorViewException;
51
import eu.etaxonomy.cdm.persistence.dao.description.IDescriptionDao;
52
import eu.etaxonomy.cdm.persistence.dao.hibernate.common.IdentifiableDaoBase;
53
import eu.etaxonomy.cdm.persistence.dto.SortableTaxonNodeQueryResult;
54
import eu.etaxonomy.cdm.persistence.dto.TermDto;
55
import eu.etaxonomy.cdm.persistence.query.MatchMode;
56
import eu.etaxonomy.cdm.persistence.query.OrderHint;
57

    
58
@Repository
59
@Qualifier("descriptionDaoImpl")
60
public class DescriptionDaoImpl
61
            extends IdentifiableDaoBase<DescriptionBase>
62
            implements IDescriptionDao{
63

    
64
    private static final Logger logger = LogManager.getLogger(DescriptionDaoImpl.class);
65

    
66
    public DescriptionDaoImpl() {
67
        super(DescriptionBase.class);
68
        indexedClasses = new Class[3];
69
        indexedClasses[0] = TaxonDescription.class;
70
        indexedClasses[1] = TaxonNameDescription.class;
71
        indexedClasses[2] = SpecimenDescription.class;
72
    }
73

    
74
//    @Override  //Override for testing
75
//    public DescriptionBase load(UUID uuid, List<String> propertyPaths){
76
//    	DescriptionBase bean = findByUuid(uuid);
77
//        if(bean == null){
78
//            return bean;
79
//        }
80
//        defaultBeanInitializer.initialize(bean, propertyPaths);
81
//
82
//        return bean;
83
//    }
84

    
85
    @Override
86
    public long countDescriptionByDistribution(Set<NamedArea> namedAreas, PresenceAbsenceTerm status) {
87
        checkNotInPriorView("DescriptionDaoImpl.countDescriptionByDistribution(Set<NamedArea> namedAreas, PresenceAbsenceTermBase status)");
88
        Query<Long> query = null;
89

    
90
        if(status == null) {
91
            query = getSession().createQuery("select count(distinct description) from TaxonDescription description left join description.descriptionElements element join element.area area where area in (:namedAreas)", Long.class);
92
        } else {
93
            query = getSession().createQuery("select count(distinct description) from TaxonDescription description left join description.descriptionElements element join element.area area  join element.status status where area in (:namedAreas) and status = :status", Long.class);
94
            query.setParameter("status", status);
95
        }
96
        query.setParameterList("namedAreas", namedAreas);
97

    
98
        return query.uniqueResult();
99
    }
100

    
101
    @Override
102
    public <T extends DescriptionElementBase> long countDescriptionElements(DescriptionBase description, Set<Feature> features, Class<T> clazz) {
103
        return countDescriptionElements(description, null, features, clazz);
104
    }
105

    
106
    @Override
107
    public <T extends DescriptionElementBase> long countDescriptionElements(DescriptionBase description, Class<? extends DescriptionBase> descriptionType,
108
            Set<Feature> features, Class<T> clazz) {
109
        AuditEvent auditEvent = getAuditEventFromContext();
110
        if (clazz == null){
111
            clazz = (Class<T>)DescriptionElementBase.class;
112
        }
113
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
114
            Criteria criteria = getCriteria(clazz);
115

    
116
            if(description != null) {
117
                criteria.add(Restrictions.eq("inDescription", description));
118
            }
119

    
120
            if(descriptionType != null) {
121
                criteria.createAlias("inDescription", "d").add(Restrictions.eq("d.class", descriptionType));
122
            }
123

    
124
            if(features != null && !features.isEmpty()) {
125
                criteria.add(Restrictions.in("feature", features));
126
            }
127

    
128
            criteria.setProjection(Projections.rowCount());
129

    
130
            return (Long)criteria.uniqueResult();
131
        } else {
132
            if(features != null && !features.isEmpty()) {
133
                long count = 0;
134
                for(Feature f : features) {
135
                    AuditQuery query = null;
136
                    query = makeAuditQuery(clazz, auditEvent);
137

    
138
                    if(description != null) {
139
                        query.add(AuditEntity.relatedId("inDescription").eq(description.getId()));
140
                    }
141

    
142
                    if(descriptionType != null) {
143
                        query.add(AuditEntity.property("inDescription.class").eq(descriptionType));
144
                    }
145

    
146
                    query.add(AuditEntity.relatedId("feature").eq(f.getId()));
147
                    query.addProjection(AuditEntity.id().count());
148
                    count += (Long)query.getSingleResult();
149
                }
150

    
151
                return count;
152
            } else {
153
                AuditQuery query = makeAuditQuery(clazz, auditEvent);
154

    
155
                if(description != null) {
156
                    query.add(AuditEntity.relatedId("inDescription").eq(description.getId()));
157
                }
158
                if(descriptionType != null) {
159
                    query.add(AuditEntity.property("inDescription.class").eq(descriptionType));
160
                }
161

    
162
                query.addProjection(AuditEntity.id().count());
163
                return (Long)query.getSingleResult();
164
            }
165
        }
166
    }
167

    
168
    @Override
169
    public long countDescriptions(Class<? extends DescriptionBase> clazz, Boolean hasImages, Boolean hasText, Set<Feature> features) {
170
        checkNotInPriorView("DescriptionDaoImpl.countDescriptions(Class<TYPE> type, Boolean hasImages, Boolean hasText, Set<Feature> features)");
171

    
172
        Criteria inner = getCriteria(clazz);
173

    
174
        Criteria elementsCriteria = inner.createCriteria("descriptionElements");
175
        if(hasText != null) {
176
            if(hasText) {
177
                elementsCriteria.add(Restrictions.isNotEmpty("multilanguageText"));
178
            } else {
179
                elementsCriteria.add(Restrictions.isEmpty("multilanguageText"));
180
            }
181
        }
182

    
183
        if(hasImages != null) {
184
            if(hasImages) {
185
                elementsCriteria.add(Restrictions.isNotEmpty("media"));
186
            } else {
187
                elementsCriteria.add(Restrictions.isEmpty("media"));
188
            }
189
        }
190

    
191
        if(features != null && !features.isEmpty()) {
192
            elementsCriteria.add(Restrictions.in("feature", features));
193
        }
194

    
195
        inner.setProjection(Projections.countDistinct("id"));
196

    
197
        return (Long)inner.uniqueResult();
198
    }
199

    
200
    @Override
201
    public long countTaxonDescriptions(Taxon taxon, Set<DefinedTerm> scopes,
202
            Set<NamedArea> geographicalScopes, Set<MarkerType> markerTypes, Set<DescriptionType> descriptionTypes) {
203
        AuditEvent auditEvent = getAuditEventFromContext();
204
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
205
            Criteria criteria = getCriteria(TaxonDescription.class);
206

    
207
            if(taxon != null) {
208
                criteria.add(Restrictions.eq("taxon", taxon));
209
            }
210

    
211
            if(scopes != null && !scopes.isEmpty()) {
212
                Set<Integer> scopeIds = new HashSet<>();
213
                for(DefinedTerm s : scopes) {
214
                    scopeIds.add(s.getId());
215
                }
216
                criteria.createCriteria("scopes").add(Restrictions.in("id", scopeIds));
217
            }
218

    
219
            if(geographicalScopes != null && !geographicalScopes.isEmpty()) {
220
                Set<Integer> geoScopeIds = new HashSet<>();
221
                for(NamedArea n : geographicalScopes) {
222
                    geoScopeIds.add(n.getId());
223
                }
224
                criteria.createCriteria("geoScopes").add(Restrictions.in("id", geoScopeIds));
225
            }
226

    
227
            addMarkerTypesCriterion(markerTypes, criteria);
228
            addDescriptionTypesCriterion(descriptionTypes, criteria);
229

    
230
            criteria.setProjection(Projections.rowCount());
231

    
232
            return (Long)criteria.uniqueResult();
233
        } else {
234
            if((scopes == null || scopes.isEmpty())&& (geographicalScopes == null || geographicalScopes.isEmpty()) && (markerTypes == null || markerTypes.isEmpty())) {
235
                AuditQuery query = makeAuditQuery(TaxonDescription.class,auditEvent);
236
                if(taxon != null) {
237
                    query.add(AuditEntity.relatedId("taxon").eq(taxon.getId()));
238
                }
239

    
240
                query.addProjection(AuditEntity.id().count());
241

    
242
                return (Long)query.getSingleResult();
243
            } else {
244
                throw new OperationNotSupportedInPriorViewException("countTaxonDescriptions(Taxon taxon, Set<Scope> scopes,Set<NamedArea> geographicalScopes)");
245
            }
246
        }
247
    }
248

    
249
    private void addDescriptionTypesCriterion(Set<DescriptionType> descriptionTypes, Criteria criteria) {
250
        if(descriptionTypes != null && !descriptionTypes.isEmpty()) {
251
            Set<Criterion> typeCriteria = new HashSet<>();
252
            for (DescriptionType descriptionType : descriptionTypes) {
253
                typeCriteria.add(Restrictions.sqlRestriction("{alias}.types like '%"+descriptionType.getKey()+"%'"));
254
            }
255
            criteria.add(Restrictions.and(typeCriteria.toArray(new Criterion[]{})));
256
        }
257
    }
258

    
259
    /**
260
     * @param markerTypes
261
     * @param criteria
262
     *
263
     */
264
    //TODO move to AnnotatableEntityDao(?)
265
    private void addMarkerTypesCriterion(Set<MarkerType> markerTypes, Criteria criteria) {
266

    
267
        if(markerTypes != null && !markerTypes.isEmpty()) {
268
            Set<Integer> markerTypeIds = new HashSet<Integer>();
269
            for(MarkerType markerType : markerTypes) {
270
                markerTypeIds.add(markerType.getId());
271
            }
272
            criteria.createCriteria("markers").add(Restrictions.eq("flag", true))
273
                    .createAlias("markerType", "mt")
274
                     .add(Restrictions.in("mt.id", markerTypeIds));
275
        } else if (markerTypes != null && markerTypes.isEmpty()){
276
            //AT: added in case the projects requires an third state description, An empty Marker type set
277
        }
278
    }
279
    @Override
280
    public <T extends DescriptionElementBase> List<T> getDescriptionElements(
281
            DescriptionBase description, Set<Feature> features,
282
            Class<T> clazz, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
283
        return getDescriptionElements(description, null, features, clazz, pageSize, pageNumber, propertyPaths);
284
    }
285

    
286
    @Override
287
    public <T extends DescriptionElementBase> List<T> getDescriptionElements(
288
            DescriptionBase description, Class<? extends DescriptionBase> descriptionType,
289
            Set<Feature> features,
290
            Class<T> clazz,
291
            Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
292

    
293
        AuditEvent auditEvent = getAuditEventFromContext();
294
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
295
            Criteria criteria = null;
296
            if(clazz == null) {
297
                criteria = getSession().createCriteria(DescriptionElementBase.class);
298
            } else {
299
                criteria = getSession().createCriteria(clazz);
300
            }
301

    
302
            if(description != null) {
303
                criteria.add(Restrictions.eq("inDescription", description));
304
            }
305
            if(descriptionType != null) {
306
                criteria.createAlias("inDescription", "d").add(Restrictions.eq("d.class", descriptionType));
307
            }
308

    
309
            if(features != null && !features.isEmpty()) {
310
                criteria.add(Restrictions.in("feature", features));
311
            }
312

    
313
            if(pageSize != null) {
314
                criteria.setMaxResults(pageSize);
315
                if(pageNumber != null) {
316
                    criteria.setFirstResult(pageNumber * pageSize);
317
                }
318
            }
319

    
320
            List<T> results = criteria.list();
321
            defaultBeanInitializer.initializeAll(results, propertyPaths);
322
            return results;
323
        } else {
324
            List<T> result = new ArrayList<T>();
325
            if(features != null && !features.isEmpty()) {
326

    
327
                for(Feature f : features) {
328
                    AuditQuery query = null;
329
                    if(clazz == null) {
330
                        query = getAuditReader().createQuery().forEntitiesAtRevision(DescriptionElementBase.class,auditEvent.getRevisionNumber());
331
                    } else {
332
                        query = getAuditReader().createQuery().forEntitiesAtRevision(clazz,auditEvent.getRevisionNumber());
333
                    }
334

    
335
                    if(description != null) {
336
                        query.add(AuditEntity.relatedId("inDescription").eq(description.getId()));
337
                    }
338

    
339
                    if(descriptionType != null) {
340
                        query.add(AuditEntity.property("inDescription.class").eq(descriptionType));
341
                    }
342

    
343
                    query.add(AuditEntity.relatedId("feature").eq(f.getId()));
344
                    result.addAll(query.getResultList());
345
                }
346
            } else {
347
                AuditQuery query = null;
348
                if(clazz == null) {
349
                    query = getAuditReader().createQuery().forEntitiesAtRevision(DescriptionElementBase.class,auditEvent.getRevisionNumber());
350
                } else {
351
                    query = getAuditReader().createQuery().forEntitiesAtRevision(clazz,auditEvent.getRevisionNumber());
352
                }
353

    
354
                if(description != null) {
355
                    query.add(AuditEntity.relatedId("inDescription").eq(description.getId()));
356
                }
357

    
358
                if(descriptionType != null) {
359
                    query.add(AuditEntity.property("inDescription.class").eq(descriptionType));
360
                }
361

    
362
                result = query.getResultList();
363
            }
364

    
365
            defaultBeanInitializer.initializeAll(result, propertyPaths);
366
            return result;
367
        }
368
    }
369

    
370
    @Override
371
    public List<TaxonDescription> listTaxonDescriptions(Taxon taxon, Set<DefinedTerm> scopes, Set<NamedArea> geographicalScopes, Set<MarkerType> markerTypes, Set<DescriptionType> descriptionTypes, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
372
        AuditEvent auditEvent = getAuditEventFromContext();
373
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
374
            Criteria criteria = getSession().createCriteria(TaxonDescription.class);
375

    
376
            if(taxon != null) {
377
                criteria.add(Restrictions.eq("taxon", taxon));
378
            }
379

    
380
            if(scopes != null && !scopes.isEmpty()) {
381
                Set<Integer> scopeIds = new HashSet<Integer>();
382
                for(DefinedTerm s : scopes) {
383
                    scopeIds.add(s.getId());
384
                }
385
                criteria.createCriteria("scopes").add(Restrictions.in("id", scopeIds));
386
            }
387

    
388
            if(geographicalScopes != null && !geographicalScopes.isEmpty()) {
389
                Set<Integer> geoScopeIds = new HashSet<Integer>();
390
                for(NamedArea n : geographicalScopes) {
391
                    geoScopeIds.add(n.getId());
392
                }
393
                criteria.createCriteria("geoScopes").add(Restrictions.in("id", geoScopeIds));
394
            }
395

    
396
            addMarkerTypesCriterion(markerTypes, criteria);
397
            addDescriptionTypesCriterion(descriptionTypes, criteria);
398

    
399
            if(pageSize != null) {
400
                criteria.setMaxResults(pageSize);
401
                if(pageNumber != null) {
402
                    criteria.setFirstResult(pageNumber * pageSize);
403
                }
404
            }
405

    
406
            List<TaxonDescription> results = criteria.list();
407

    
408
            defaultBeanInitializer.initializeAll(results, propertyPaths);
409

    
410
            return results;
411
        } else {
412
            if((scopes == null || scopes.isEmpty())&& (geographicalScopes == null || geographicalScopes.isEmpty())&& (markerTypes == null || markerTypes.isEmpty())) {
413
                AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonDescription.class,auditEvent.getRevisionNumber());
414
                if(taxon != null) {
415
                    query.add(AuditEntity.relatedId("taxon").eq(taxon.getId()));
416
                }
417

    
418
                if(pageSize != null) {
419
                    query.setMaxResults(pageSize);
420
                    if(pageNumber != null) {
421
                        query.setFirstResult(pageNumber * pageSize);
422
                    } else {
423
                        query.setFirstResult(0);
424
                    }
425
                }
426

    
427
                List<TaxonDescription> results = query.getResultList();
428
                defaultBeanInitializer.initializeAll(results, propertyPaths);
429
                return results;
430
            } else {
431
                throw new OperationNotSupportedInPriorViewException("countTaxonDescriptions(Taxon taxon, Set<Scope> scopes,Set<NamedArea> geographicalScopes)");
432
            }
433
        }
434
    }
435

    
436
    @Override
437
    public List<TaxonNameDescription> getTaxonNameDescriptions(TaxonName name, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
438
        AuditEvent auditEvent = getAuditEventFromContext();
439
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
440
            Criteria criteria = getSession().createCriteria(TaxonNameDescription.class);
441

    
442
          if(name != null) {
443
              criteria.add(Restrictions.eq("taxonName", name));
444
          }
445

    
446
          if(pageSize != null) {
447
              criteria.setMaxResults(pageSize);
448
              if(pageNumber != null) {
449
                  criteria.setFirstResult(pageNumber * pageSize);
450
              }
451
          }
452

    
453
          List<TaxonNameDescription> results = criteria.list();
454

    
455
          defaultBeanInitializer.initializeAll(results, propertyPaths);
456

    
457
          return results;
458
        } else {
459
            AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(TaxonNameDescription.class,auditEvent.getRevisionNumber());
460

    
461
            if(name != null) {
462
                query.add(AuditEntity.relatedId("taxonName").eq(name.getId()));
463
            }
464

    
465
            if(pageSize != null) {
466
                  query.setMaxResults(pageSize);
467
                  if(pageNumber != null) {
468
                      query.setFirstResult(pageNumber * pageSize);
469
                  }
470
            }
471

    
472
            List<TaxonNameDescription> results = query.getResultList();
473
            defaultBeanInitializer.initializeAll(results, propertyPaths);
474
            return results;
475
        }
476
    }
477

    
478
    @Override
479
    public long countTaxonNameDescriptions(TaxonName name) {
480
        AuditEvent auditEvent = getAuditEventFromContext();
481
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
482
            Criteria criteria = getCriteria(TaxonNameDescription.class);
483

    
484
            if(name != null) {
485
                criteria.add(Restrictions.eq("taxonName", name));
486
            }
487

    
488
            criteria.setProjection(Projections.rowCount());
489

    
490
            return (Long)criteria.uniqueResult();
491
        } else {
492
            AuditQuery query = makeAuditQuery(TaxonNameDescription.class,auditEvent);
493

    
494
            if(name != null) {
495
                query.add(AuditEntity.relatedId("taxonName").eq(name.getId()));
496
            }
497

    
498
            query.addProjection(AuditEntity.id().count());
499
            return (Long)query.getSingleResult();
500
        }
501
    }
502

    
503
    /**
504
     * Should use a DetachedCriteria & subquery, but HHH-158 prevents this, for now.
505
     *
506
     * e.g. DetachedCriteria inner = DestachedCriteria.forClass(type);
507
     *
508
     * outer.add(Subqueries.propertyIn("id", inner));
509
     */
510
    @Override
511
    public List<DescriptionBase> listDescriptions(Class<? extends DescriptionBase> clazz, Boolean hasImages, Boolean hasText,	Set<Feature> features, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
512
        checkNotInPriorView("DescriptionDaoImpl.listDescriptions(Class<TYPE> type, Boolean hasImages, Boolean hasText,	Set<Feature> features, Integer pageSize, Integer pageNumber)");
513
        Criteria inner = getCriteria(clazz);
514

    
515
        Criteria elementsCriteria = inner.createCriteria("descriptionElements");
516
        if(hasText != null) {
517
            if(hasText) {
518
                elementsCriteria.add(Restrictions.isNotEmpty("multilanguageText"));
519
            } else {
520
                elementsCriteria.add(Restrictions.isEmpty("multilanguageText"));
521
            }
522
        }
523

    
524
        if(hasImages != null) {
525
            if(hasImages) {
526
                elementsCriteria.add(Restrictions.isNotEmpty("media"));
527
            } else {
528
                elementsCriteria.add(Restrictions.isEmpty("media"));
529
            }
530
        }
531

    
532
        if(features != null && !features.isEmpty()) {
533
            elementsCriteria.add(Restrictions.in("feature", features));
534
        }
535

    
536
        inner.setProjection(Projections.distinct(Projections.id()));
537

    
538
        @SuppressWarnings("unchecked")
539
        List<Object> intermediateResult = inner.list();
540

    
541
        if(intermediateResult.isEmpty()) {
542
            return new ArrayList<>();
543
        }
544

    
545
        Integer[] resultIds = new Integer[intermediateResult.size()];
546
        for(int i = 0; i < resultIds.length; i++) {
547
                resultIds[i] = (Integer)intermediateResult.get(i);
548
        }
549

    
550
        Criteria outer = getCriteria(clazz);
551

    
552
        outer.add(Restrictions.in("id", resultIds));
553
        addOrder(outer, orderHints);
554

    
555
        addPageSizeAndNumber(outer, pageSize, pageNumber);
556

    
557
        @SuppressWarnings({ "unchecked", "rawtypes" })
558
        List<DescriptionBase> results = outer.list();
559
        defaultBeanInitializer.initializeAll(results, propertyPaths);
560
        return results;
561
    }
562

    
563
    @Override
564
    public List<TaxonDescription> searchDescriptionByDistribution(Set<NamedArea> namedAreas, PresenceAbsenceTerm status, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
565
        checkNotInPriorView("DescriptionDaoImpl.searchDescriptionByDistribution(Set<NamedArea> namedAreas, PresenceAbsenceTermBase status, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths)");
566

    
567
        Criteria criteria = getSession().createCriteria(TaxonDescription.class);
568
        Criteria elements = criteria.createCriteria("descriptionElements", "descriptionElement", Criteria.LEFT_JOIN);
569
        elements.add(Restrictions.in("area", namedAreas.toArray()));
570

    
571
        if(status != null) {
572
            elements.add(Restrictions.eq("status", status));
573
        }
574

    
575
        ProjectionList projectionList = Projections.projectionList().add(Projections.id());
576

    
577
        if(orderHints != null && !orderHints.isEmpty()) {
578
            for(OrderHint orderHint : orderHints) {
579
                projectionList = projectionList.add(Projections.property(orderHint.getPropertyName()));
580
            }
581
        }
582

    
583
        criteria.setProjection(Projections.distinct(projectionList));
584

    
585
        if(pageSize != null) {
586
            criteria.setMaxResults(pageSize);
587
            if(pageNumber != null) {
588
                criteria.setFirstResult(pageNumber * pageSize);
589
            }
590
        }
591

    
592
        addOrder(criteria,orderHints);
593

    
594
        @SuppressWarnings("unchecked")
595
        List<Object> intermediateResult = criteria.list();
596

    
597
        if(intermediateResult.isEmpty()) {
598
            return new ArrayList<>();
599
        }
600

    
601
        Integer[] resultIds = new Integer[intermediateResult.size()];
602
        for(int i = 0; i < resultIds.length; i++) {
603
            if(orderHints == null || orderHints.isEmpty()) {
604
                resultIds[i] = (Integer)intermediateResult.get(i);
605
            } else {
606
              resultIds[i] = ((Number)((Object[])intermediateResult.get(i))[0]).intValue();
607
            }
608
        }
609

    
610
        criteria = getSession().createCriteria(TaxonDescription.class);
611
        criteria.add(Restrictions.in("id", resultIds));
612
        addOrder(criteria,orderHints);
613

    
614
        @SuppressWarnings("unchecked")
615
        List<TaxonDescription> results = criteria.list();
616
        defaultBeanInitializer.initializeAll(results, propertyPaths);
617
        return results;
618
    }
619

    
620
    @Override
621
    public List<CommonTaxonName> searchDescriptionByCommonName(String queryString, MatchMode matchMode, Integer pageSize, Integer pageNumber) {
622

    
623
        Criteria crit = getSession().createCriteria(CommonTaxonName.class);
624
        if (matchMode == MatchMode.EXACT) {
625
            crit.add(Restrictions.eq("name", matchMode.queryStringFrom(queryString)));
626
        } else {
627
            crit.add(Restrictions.ilike("name", matchMode.queryStringFrom(queryString)));
628
        }
629

    
630
        if(pageSize != null) {
631
            crit.setMaxResults(pageSize);
632
            if(pageNumber != null) {
633
                crit.setFirstResult(pageNumber * pageSize);
634
            }
635
        }
636
        @SuppressWarnings("unchecked")
637
        List<CommonTaxonName> results = crit.list();
638
        return results;
639
    }
640

    
641
    @Override
642
    public Integer countDescriptionByCommonName(String queryString, MatchMode matchMode) {
643
        //TODO inprove performance
644
        List<CommonTaxonName> results =  searchDescriptionByCommonName(queryString, matchMode, null, null);
645
        return results.size();
646
    }
647

    
648
    @Override
649
    public DescriptionBase find(LSID lsid) {
650
        DescriptionBase<?> descriptionBase = super.find(lsid);
651
        if(descriptionBase != null) {
652
            List<String> propertyPaths = new ArrayList<>();
653
            propertyPaths.add("createdBy");
654
            propertyPaths.add("updatedBy");
655
            propertyPaths.add("taxon");
656
            propertyPaths.add("taxonName");
657
            propertyPaths.add("descriptionElements");
658
            propertyPaths.add("descriptionElements.createdBy");
659
            propertyPaths.add("descriptionElements.updatedBy");
660
            propertyPaths.add("descriptionElements.feature");
661
            propertyPaths.add("descriptionElements.multilanguageText");
662
            propertyPaths.add("descriptionElements.multilanguageText.language");
663
            propertyPaths.add("descriptionElements.area");
664
            propertyPaths.add("descriptionElements.status");
665
            propertyPaths.add("descriptionElements.modifyingText");
666
            propertyPaths.add("descriptionElementsmodifyingText.language");
667
            propertyPaths.add("descriptionElements.modifiers");
668

    
669
            defaultBeanInitializer.initialize(descriptionBase, propertyPaths);
670
        }
671
        return descriptionBase;
672
    }
673

    
674

    
675
    @Override
676
    public List<Integer> getIndividualAssociationSpecimenIDs(UUID taxonUuid,
677
            Set<Feature> features, Integer pageSize,
678
            Integer pageNumber, List<String> propertyPaths){
679
        Query<Integer> query = prepareGetDescriptionElementForTaxon(taxonUuid, features, IndividualsAssociation.class, pageSize, pageNumber, "de.associatedSpecimenOrObservation.id");
680
        List<Integer> results = query.list();
681
        return results;
682
    }
683

    
684
    @Override
685
    public List<SortableTaxonNodeQueryResult> getNodeOfIndividualAssociationForSpecimen(UUID specimenUuid, UUID classificationUuid){
686
        String selectString = " new " +SortableTaxonNodeQueryResult.class.getName()+"(n.uuid, n.id, n.treeIndex, t.uuid, t.titleCache, t.name.titleCache, t.name.rank, n.parent.uuid) ";
687
        Query<SortableTaxonNodeQueryResult> query = prepareGetIndividualAssociationForSpecimen(specimenUuid, classificationUuid, selectString);
688
        @SuppressWarnings("unchecked")
689
        List<SortableTaxonNodeQueryResult> results = query.list();
690
        return results;
691
    }
692

    
693
    @Override
694
    public <T extends DescriptionElementBase> List<T> getDescriptionElementForTaxon(
695
            UUID taxonUuid, Set<Feature> features,
696
            Class<T> type, Integer pageSize,
697
            Integer pageNumber, List<String> propertyPaths) {
698

    
699
//      Logger.getLogger("org.hibernate.SQL").setLevel(Level.TRACE);
700
        Query<T> query = prepareGetDescriptionElementForTaxon(taxonUuid, features, type, pageSize, pageNumber, "de");
701

    
702
        if (logger.isDebugEnabled()){logger.debug(" dao: get list ...");}
703
        List<T> results = query.list();
704
        if (logger.isDebugEnabled()){logger.debug(" dao: initialize ...");}
705
        defaultBeanInitializer.initializeAll(results, propertyPaths);
706
        if (logger.isDebugEnabled()){logger.debug(" dao: initialize - DONE");}
707

    
708
//        Logger.getLogger("org.hibernate.SQL").setLevel(Level.WARN);
709
        return results;
710
    }
711

    
712
    @Override
713
    public <T extends DescriptionElementBase> long countDescriptionElementForTaxon(
714
            UUID taxonUuid, Set<Feature> features, Class<T> type) {
715

    
716
        Query<Long> query = prepareGetDescriptionElementForTaxon(taxonUuid, features, type, null, null, "count(de)");
717

    
718
        return query.uniqueResult();
719
    }
720

    
721
    private <T extends DescriptionElementBase, R extends Object> Query<R> prepareGetDescriptionElementForTaxon(UUID taxonUuid,
722
            Set<Feature> features, Class<T> type, Integer pageSize, Integer pageNumber, String selectString) {
723

    
724
        String queryString = "SELECT " + selectString + " FROM DescriptionElementBase AS de" +
725
                " LEFT JOIN de.inDescription AS d" +
726
                " LEFT JOIN d.taxon AS t" +
727
                " WHERE d.class = 'TaxonDescription' AND t.uuid = :taxon_uuid ";
728

    
729
        if(type != null){
730
            queryString += " and de.class = :type";
731
        }
732
        if (features != null && features.size() > 0){
733
            queryString += " and de.feature in (:features) ";
734
        }
735
        Query<R> query = getSession().createQuery(queryString);
736

    
737
        query.setParameter("taxon_uuid", taxonUuid);
738
        if(type != null){
739
            query.setParameter("type", type.getSimpleName());
740
        }
741
        if(features != null && features.size() > 0){
742
            query.setParameterList("features", features) ;
743
        }
744

    
745
        addPageSizeAndNumber(query, pageSize, pageNumber);
746
        return query;
747
    }
748

    
749
    private Query<SortableTaxonNodeQueryResult> prepareGetIndividualAssociationForSpecimen(UUID specimenUuid, UUID classificationUuid, String selectString) {
750

    
751
        String queryString = "SELECT " + selectString + " FROM DescriptionElementBase AS de" +
752
                " LEFT JOIN de.inDescription AS d" +
753
                " LEFT JOIN d.taxon AS t" +
754
                " LEFT JOIN t.taxonNodes AS n" +
755
                " LEFT JOIN de.associatedSpecimenOrObservation AS specimen ";
756
        String classificationString = "";
757
        if (classificationUuid != null){
758
            classificationString = " LEFT JOIN n.classification AS c ";
759
        }
760
        String whereString = " WHERE specimen.uuid = :specimen_uuid";
761
        if (classificationUuid != null){
762
            whereString = whereString + " AND c.uuid = :classifcationUuid";
763
        }
764

    
765
        Query<SortableTaxonNodeQueryResult> query = getSession().createQuery(queryString + classificationString + whereString, SortableTaxonNodeQueryResult.class);
766

    
767
        query.setParameter("specimen_uuid", specimenUuid);
768
        if (classificationUuid != null){
769
            query.setParameter("classifcationUuid", classificationUuid);
770
        }
771

    
772
        return query;
773
    }
774

    
775
    @Override
776
    public List<Media> listTaxonDescriptionMedia(UUID taxonUuid,
777
            Boolean limitToGalleries, Set<MarkerType> markerTypes,
778
            Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
779

    
780
               AuditEvent auditEvent = getAuditEventFromContext();
781
            if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
782
                String queryString = " SELECT media " +
783
                    getTaxonDescriptionMediaQueryString(
784
                        taxonUuid, limitToGalleries,  markerTypes);
785
                queryString +=
786
                    " GROUP BY media "
787
//	    						" ORDER BY index(media) "  //not functional
788
                    ;
789

    
790
                Query<Media> query = getSession().createQuery(queryString, Media.class);
791

    
792
                setTaxonDescriptionMediaParameters(query, taxonUuid, limitToGalleries, markerTypes);
793
//	            addMarkerTypesCriterion(markerTypes, hql);
794

    
795
                addPageSizeAndNumber(query, pageSize, pageNumber);
796
                List<Media> results = query.list();
797
                defaultBeanInitializer.initializeAll(results, propertyPaths);
798

    
799
                return results;
800
            } else {
801
                throw new OperationNotSupportedInPriorViewException("countTaxonDescriptionMedia(UUID taxonUuid, boolean restrictToGalleries)");
802
            }
803
    }
804

    
805
    @Override
806
    public int countTaxonDescriptionMedia(UUID taxonUuid,
807
            Boolean limitToGalleries, Set<MarkerType> markerTypes) {
808
        AuditEvent auditEvent = getAuditEventFromContext();
809
        if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
810
            String queryString = " SELECT count(DISTINCT media) " +
811
                getTaxonDescriptionMediaQueryString(
812
                    taxonUuid, limitToGalleries, markerTypes);
813

    
814
            Query<Long> query = getSession().createQuery(queryString, Long.class);
815
            setTaxonDescriptionMediaParameters(query, taxonUuid, limitToGalleries, markerTypes);
816
            return query.uniqueResult().intValue();
817
        }else{
818
            throw new OperationNotSupportedInPriorViewException("countTaxonDescriptionMedia(UUID taxonUuid)");
819
        }
820

    
821
    }
822

    
823
    private void setTaxonDescriptionMediaParameters(Query query, UUID taxonUuid, Boolean limitToGalleries, Set<MarkerType> markerTypes) {
824
        if(taxonUuid != null){
825
            query.setParameter("uuid", taxonUuid);
826
        }
827
    }
828

    
829
    /**
830
     * @param taxonUuid
831
     * @param restrictToGalleries
832
     * @param markerTypes
833
     * @return
834
     */
835
    private String getTaxonDescriptionMediaQueryString(UUID taxonUuid,
836
            Boolean restrictToGalleries, Set<MarkerType> markerTypes) {
837
        String fromQueryString =
838
            " FROM DescriptionElementBase as deb INNER JOIN " +
839
                " deb.inDescription as td "
840
                + " INNER JOIN td.taxon as t "
841
                + " JOIN deb.media as media "
842
                + " LEFT JOIN td.markers marker ";
843

    
844
        String whereQueryString = " WHERE (1=1) ";
845
        if (taxonUuid != null){
846
            whereQueryString += " AND t.uuid = :uuid ";
847
        }
848
        if (restrictToGalleries){
849
            whereQueryString += " AND td.imageGallery is true ";
850
        }
851
        if (markerTypes != null && !markerTypes.isEmpty()){
852
            whereQueryString += " AND (1=0";
853
            for (MarkerType markerType : markerTypes){
854
                whereQueryString += " OR ( marker.markerType.id = " + markerType.getId() + " AND marker.flag is true)";
855

    
856
            }
857
            whereQueryString += ") ";
858
        }
859

    
860
        return fromQueryString + whereQueryString;
861

    
862
    }
863

    
864
    @SuppressWarnings("unchecked")
865
    @Override
866
    public List<TermDto> listNamedAreasInUse(boolean includeAllParents, Integer pageSize, Integer pageNumber) {
867

    
868
//        Logger.getLogger("org.hibernate.SQL").setLevel(Level.TRACE);
869

    
870
        StringBuilder queryString = new StringBuilder(
871
                "SELECT DISTINCT a.id, a.partOf.id"
872
                + " FROM Distribution AS d JOIN d.area AS a");
873
        Query<Object[]> query = getSession().createQuery(queryString.toString(), Object[].class);
874

    
875
        List<Object[]> areasInUse = query.list();
876
        List<Object[]> parentResults = new ArrayList<>();
877

    
878
        if(!areasInUse.isEmpty()) {
879
            Set<Object> allAreaIds = new HashSet<>(areasInUse.size());
880

    
881
            if(includeAllParents) {
882
                // find all parent nodes
883
                String allAreasQueryStr = "select a.id, a.partOf.id from NamedArea as a";
884
                query = getSession().createQuery(allAreasQueryStr, Object[].class);
885
                List<Object[]> allAreasResult = query.list();
886
                Map<Object, Object> allAreasMap = ArrayUtils.toMap(allAreasResult.toArray());
887

    
888
                Set<Object> parents = new HashSet<>();
889

    
890
                for(Object[] leaf : areasInUse) {
891
                    allAreaIds.add(leaf[0]);
892
                    Object parentId = leaf[1];
893
                    while (parentId != null) {
894
                        if(parents.contains(parentId)) {
895
                            // break if the parent already is in the set
896
                            break;
897
                        }
898
                        parents.add(parentId);
899
                        parentId = allAreasMap.get(parentId);
900
                    }
901
                }
902
                allAreaIds.addAll(parents);
903
            } else {
904
                // only add the ids found so far
905
                for(Object[] leaf : areasInUse) {
906
                    allAreaIds.add(leaf[0]);
907
                }
908
            }
909

    
910
            // NOTE can't use "select new TermDto(distinct a.uuid, r , a.vocabulary.uuid) since we will get multiple
911
            // rows for a term with multiple representations
912
            String parentAreasQueryStr = TermDto.getTermDtoSelect("NamedArea")
913
                    + "where a.id in (:allAreaIds) order by a.idInVocabulary";
914
            query = getSession().createQuery(parentAreasQueryStr, Object[].class);
915
            query.setParameterList("allAreaIds", allAreaIds);
916

    
917
            addPageSizeAndNumber(query, pageSize, pageNumber);
918
            parentResults = query.list();
919
        }
920
        List<TermDto> dtoList = TermDto.termDtoListFrom(parentResults);
921

    
922
        return dtoList;
923
    }
924

    
925

    
926

    
927

    
928

    
929
}
(1-1/7)