Project

General

Profile

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

    
11
import java.util.ArrayList;
12
import java.util.Collection;
13
import java.util.Enumeration;
14
import java.util.HashMap;
15
import java.util.Iterator;
16
import java.util.List;
17
import java.util.Locale;
18
import java.util.Map;
19
import java.util.Set;
20
import java.util.UUID;
21

    
22
import org.apache.commons.lang.StringUtils;
23
import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
24
import org.hibernate.Criteria;
25
import org.hibernate.Session;
26
import org.hibernate.criterion.Criterion;
27
import org.hibernate.criterion.Disjunction;
28
import org.hibernate.criterion.Order;
29
import org.hibernate.criterion.Projections;
30
import org.hibernate.criterion.Restrictions;
31
import org.hibernate.envers.query.AuditEntity;
32
import org.hibernate.envers.query.AuditQuery;
33
import org.hibernate.query.Query;
34
import org.springframework.dao.DataAccessException;
35
import org.springframework.stereotype.Repository;
36

    
37
import eu.etaxonomy.cdm.common.URI;
38
import eu.etaxonomy.cdm.model.common.AnnotationType;
39
import eu.etaxonomy.cdm.model.common.CdmBase;
40
import eu.etaxonomy.cdm.model.common.ExtensionType;
41
import eu.etaxonomy.cdm.model.common.Language;
42
import eu.etaxonomy.cdm.model.common.MarkerType;
43
import eu.etaxonomy.cdm.model.description.MeasurementUnit;
44
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
45
import eu.etaxonomy.cdm.model.description.State;
46
import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
47
import eu.etaxonomy.cdm.model.description.TextFormat;
48
import eu.etaxonomy.cdm.model.location.Country;
49
import eu.etaxonomy.cdm.model.location.NamedArea;
50
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
51
import eu.etaxonomy.cdm.model.location.NamedAreaType;
52
import eu.etaxonomy.cdm.model.location.ReferenceSystem;
53
import eu.etaxonomy.cdm.model.media.Media;
54
import eu.etaxonomy.cdm.model.media.RightsType;
55
import eu.etaxonomy.cdm.model.metadata.TermSearchField;
56
import eu.etaxonomy.cdm.model.name.HybridRelationshipType;
57
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
58
import eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus;
59
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
60
import eu.etaxonomy.cdm.model.name.Rank;
61
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
62
import eu.etaxonomy.cdm.model.occurrence.DerivationEventType;
63
import eu.etaxonomy.cdm.model.taxon.SynonymType;
64
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
65
import eu.etaxonomy.cdm.model.term.DefinedTerm;
66
import eu.etaxonomy.cdm.model.term.DefinedTermBase;
67
import eu.etaxonomy.cdm.model.term.TermType;
68
import eu.etaxonomy.cdm.model.term.TermVocabulary;
69
import eu.etaxonomy.cdm.model.view.AuditEvent;
70
import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
71
import eu.etaxonomy.cdm.persistence.dao.hibernate.common.IdentifiableDaoBase;
72
import eu.etaxonomy.cdm.persistence.dao.term.IDefinedTermDao;
73
import eu.etaxonomy.cdm.persistence.dto.FeatureDto;
74
import eu.etaxonomy.cdm.persistence.dto.TermCollectionDto;
75
import eu.etaxonomy.cdm.persistence.dto.TermDto;
76
import eu.etaxonomy.cdm.persistence.dto.TermVocabularyDto;
77
import eu.etaxonomy.cdm.persistence.query.MatchMode;
78
import eu.etaxonomy.cdm.persistence.query.OrderHint;
79

    
80
/**
81
 * @author a.kohlbecker
82
 * @since 29.05.2008
83
 */
84
@Repository
85
public class DefinedTermDaoImpl
86
        extends IdentifiableDaoBase<DefinedTermBase>
87
        implements IDefinedTermDao{
88

    
89
    private static final Logger logger = LogManager.getLogger(DefinedTermDaoImpl.class);
90

    
91
	@SuppressWarnings("unchecked")
92
    public DefinedTermDaoImpl() {
93
		super(DefinedTermBase.class);
94
		indexedClasses = new Class[25];
95
		indexedClasses[0] = Rank.class;
96
		indexedClasses[1] = AnnotationType.class;
97
		indexedClasses[2] = ExtensionType.class;
98
		indexedClasses[3] = Language.class;
99
		indexedClasses[4] = MarkerType.class;
100
		indexedClasses[5] = MeasurementUnit.class;
101
		indexedClasses[6] = DefinedTerm.class;
102
		indexedClasses[7] = PresenceAbsenceTerm.class;
103
		indexedClasses[8] = State.class;
104
		indexedClasses[9] = StatisticalMeasure.class;
105
		indexedClasses[10] = TextFormat.class;
106
		indexedClasses[11] = DerivationEventType.class;
107
		indexedClasses[12] = NamedArea.class;
108
		indexedClasses[13] = NamedAreaLevel.class;
109
		indexedClasses[14] = NamedAreaType.class;
110
		indexedClasses[15] = ReferenceSystem.class;
111
		indexedClasses[16] = Country.class;
112
		indexedClasses[17] = RightsType.class;
113
		indexedClasses[18] = HybridRelationshipType.class;
114
		indexedClasses[19] = NameRelationshipType.class;
115
		indexedClasses[20] = NameTypeDesignationStatus.class;
116
		indexedClasses[21] = NomenclaturalStatusType.class;
117
		indexedClasses[22] = SpecimenTypeDesignationStatus.class;
118
		indexedClasses[23] = SynonymType.class;
119
		indexedClasses[24] = TaxonRelationshipType.class;
120
	}
121

    
122
	/**
123
	 * Searches by Label
124
	 */
125
	@Override
126
    public List<DefinedTermBase> findByLabel(String queryString) {
127
		return findByLabel(queryString, null);
128
	}
129

    
130
	/**
131
	 * Searches by Label
132
	 */
133
	@Override
134
    public List<DefinedTermBase> findByLabel(String queryString, CdmBase sessionObject) {
135
		checkNotInPriorView("DefinedTermDaoImpl.findByTitle(String queryString, CdmBase sessionObject)");
136
		Session session = getSession();
137
		if ( sessionObject != null ) {//attache the object to the session, TODO needed?
138
			session.update(sessionObject);
139
		}
140
		Query<DefinedTermBase> query = session.createQuery("SELECT term "
141
		        + " FROM DefinedTermBase term JOIN FETCH term.representations representation "
142
		        + " WHERE representation.label = :label",
143
		        DefinedTermBase.class);
144
		query.setParameter("label", queryString);
145
		@SuppressWarnings("rawtypes")
146
		List<DefinedTermBase> result = deduplicateResult(query.list());
147
		return result;
148
	}
149

    
150
	@Override
151
    public List<DefinedTermBase> findByTitle(String queryString, MatchMode matchMode, int page, int pagesize, List<Criterion> criteria) {
152
		//FIXME is query parametrised?
153
		checkNotInPriorView("DefinedTermDaoImpl.findByTitle(String queryString, ITitledDao.MATCH_MODE matchMode, int page, int pagesize, List<Criterion> criteria)");
154
		Criteria crit = getSession().createCriteria(type);
155
		crit.add(Restrictions.ilike("titleCache", matchMode.queryStringFrom(queryString)));
156
		crit.setMaxResults(pagesize);
157
		int firstItem = (page - 1) * pagesize + 1;
158
		crit.setFirstResult(firstItem);
159
		@SuppressWarnings("unchecked")
160
        List<DefinedTermBase> results = deduplicateResult(crit.list());
161
		return results;
162
	}
163

    
164
	@Override
165
    public Country getCountryByIso(String iso3166) {
166
		// If iso639 = "" query returns non-unique result. We prevent this here:
167
		if (StringUtils.isBlank(iso3166) || iso3166.length()<2 || iso3166.length()>3) { return null; }
168
		AuditEvent auditEvent = getAuditEventFromContext();
169
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
170
		    Query<Country> query = getSession().createQuery("FROM Country WHERE iso3166_A2 = :isoCode OR idInVocabulary = :isoCode", Country.class);
171
		    query.setParameter("isoCode", iso3166);
172
		    return query.uniqueResult();
173
		} else {
174
			AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(Country.class,auditEvent.getRevisionNumber());
175
			query.add(AuditEntity.property("iso3166_A2").eq(iso3166));
176
			query.add(AuditEntity.property("idInVocabulary").eq(iso3166));
177
			return (Country) query.getSingleResult();
178
		}
179
	}
180

    
181
	@Override
182
    public <T extends DefinedTermBase> List<T> getDefinedTermByRepresentationText(String text, Class<T> clazz ) {
183
		return getDefinedTermByRepresentationText(text,clazz,null,null);
184
	}
185

    
186
	@Override
187
    public <T extends DefinedTermBase> List<T> getDefinedTermByRepresentationText(String text, Class<T> clazz, Integer pageSize,Integer  pageNumber) {
188
		checkNotInPriorView("DefinedTermDaoImpl.getDefinedTermByRepresentationText(String text, Class<T> clazz, Integer pageSize,Integer  pageNumber)");
189

    
190
		Criteria criteria = getCriteria(clazz);
191

    
192
		criteria.createAlias("representations", "r").add(Restrictions.like("r.text", text));
193

    
194
		addPageSizeAndNumber(criteria, pageSize, pageNumber);
195

    
196
		@SuppressWarnings("unchecked")
197
        List<T> result = deduplicateResult(criteria.list());
198
		return result;
199
	}
200

    
201
	@Override
202
    public long countDefinedTermByRepresentationText(String text, Class<? extends DefinedTermBase> clazz) {
203
	    checkNotInPriorView("DefinedTermDaoImpl.countDefinedTermByRepresentationText(String text, Class<? extends DefinedTermBase> clazz)");
204
		Criteria criteria = getCriteria(clazz);
205

    
206
		criteria.createAlias("representations", "r").add(Restrictions.like("r.text", text));
207

    
208
		criteria.setProjection(Projections.rowCount());
209

    
210
		return (Long)criteria.uniqueResult();
211
	}
212

    
213
	@Override
214
	public <T extends DefinedTermBase> List<T> getDefinedTermByIdInVocabulary(String label, UUID vocUuid, Class<T> clazz, Integer pageSize, Integer pageNumber) {
215
		checkNotInPriorView("DefinedTermDaoImpl.getDefinedTermByIdInVocabulary(String label, UUID vocUuid, Class<T> clazz, Integer pageSize, Integer pageNumber)");
216

    
217
		Criteria criteria = getCriteria(clazz);
218

    
219
		criteria.createAlias("vocabulary", "voc")
220
		    .add(Restrictions.like("voc.uuid", vocUuid))
221
			.add(Restrictions.like("idInVocabulary", label, org.hibernate.criterion.MatchMode.EXACT));
222

    
223
		addPageSizeAndNumber(criteria, pageSize, pageNumber);
224

    
225
		@SuppressWarnings("unchecked")
226
        List<T> result = deduplicateResult(criteria.list());
227
		return result;
228
	}
229

    
230
    @Override
231
	public <T extends DefinedTermBase> List<T> getDefinedTermByRepresentationAbbrev(String text, Class<T> clazz, Integer pageSize,Integer  pageNumber) {
232
		checkNotInPriorView("DefinedTermDaoImpl.getDefinedTermByRepresentationAbbrev(String abbrev, Class<T> clazz, Integer pageSize,Integer  pageNumber)");
233

    
234
		Criteria criteria = getCriteria(clazz);
235

    
236
		criteria.createAlias("representations", "r").add(Restrictions.like("r.abbreviatedLabel", text));
237

    
238
		addPageSizeAndNumber(criteria, pageSize, pageNumber);
239

    
240
		@SuppressWarnings("unchecked")
241
		List<T> result = deduplicateResult(criteria.list());
242
		return result;
243
	}
244

    
245
	@Override
246
	public long countDefinedTermByRepresentationAbbrev(String text, Class<? extends DefinedTermBase> clazz) {
247
	    checkNotInPriorView("DefinedTermDaoImpl.countDefinedTermByRepresentationAbbrev(String abbrev, Class<? extends DefinedTermBase> clazz)");
248
		Criteria criteria = getCriteria(clazz);
249

    
250
		criteria.createAlias("representations", "r").add(Restrictions.like("r.abbreviatedLabel", text));
251
		criteria.setProjection(Projections.rowCount());
252

    
253
        return (Long)criteria.uniqueResult();
254
	}
255

    
256
	@Override
257
    public Language getLanguageByIso(String iso639) {
258
		if (iso639.length() < 2 || iso639.length() > 3) {
259
			logger.warn("Invalid length " + iso639.length() + " of ISO code. Length must be 2 or 3.");
260
			return null;
261
		}
262
		boolean isIso639_1 = iso639.length() == 2;
263

    
264
		String queryStr;
265
		if (isIso639_1){
266
			queryStr = "FROM Language WHERE iso639_1 = :isoCode";
267
		}else{
268
			queryStr = "FROM Language WHERE idInVocabulary = :isoCode AND vocabulary.uuid = :vocUuid";
269
		}
270
		AuditEvent auditEvent = getAuditEventFromContext();
271
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
272
		    Query<Language> query = getSession().createQuery(queryStr, Language.class);
273
		    query.setParameter("isoCode", iso639);
274
		    if (! isIso639_1){
275
		    	query.setParameter("vocUuid", Language.uuidLanguageVocabulary);
276
			}
277
		    return query.uniqueResult();
278
		} else {
279
			AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(Language.class,auditEvent.getRevisionNumber());
280
			if (isIso639_1){
281
				query.add(AuditEntity.property("iso639_1").eq(iso639));
282
			}else{
283
				query.add(AuditEntity.property("iso639_2").eq(iso639));
284
				query.add(AuditEntity.property("vocabulary.uuid").eq(Language.uuidLanguageVocabulary));
285
			}
286

    
287
			return (Language)query.getSingleResult();
288
		}
289
	}
290

    
291
	/**
292
	 *  FIXME this will result in a query per language - could you, given that iso codes
293
	 *  are unique, use from Language where iso639_1 in (:isoCode) or iso639_2 in (:isoCode)
294
	 */
295
	@Override
296
    public List<Language> getLanguagesByIso(List<String> iso639List) {
297
		List<Language> languages = new ArrayList<>(iso639List.size());
298
		for (String iso639 : iso639List) {
299
			languages.add(getLanguageByIso(iso639));
300
		}
301
		return languages;
302
	}
303

    
304
	@Override
305
    public List<Language> getLanguagesByLocale(Enumeration<Locale> locales) {
306
		List<Language> languages = new ArrayList<>();
307
		while(locales.hasMoreElements()) {
308
			Locale locale = locales.nextElement();
309
			languages.add(getLanguageByIso(locale.getLanguage()));
310
		}
311
		return languages;
312
	}
313

    
314
	@Override
315
    public long count(NamedAreaLevel level, NamedAreaType type) {
316
		AuditEvent auditEvent = getAuditEventFromContext();
317
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
318
		    Criteria criteria = getCriteria(NamedArea.class);
319

    
320
		    if(level != null) {
321
			    criteria.add(Restrictions.eq("level",level));
322
		    }
323

    
324
		    if(type != null) {
325
			    criteria.add(Restrictions.eq("type", type));
326
		    }
327

    
328
		    criteria.setProjection(Projections.rowCount());
329

    
330
	        return (Long)criteria.uniqueResult();
331
		} else {
332
			AuditQuery query = makeAuditQuery(NamedArea.class, auditEvent);
333

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

    
338
		    if(type != null) {
339
		    	query.add(AuditEntity.relatedId("type").eq(type.getId()));
340
		    }
341
		    query.addProjection(AuditEntity.id().count());
342
		    return (Long)query.getSingleResult();
343
		}
344
	}
345

    
346
	@Override
347
    public long countMedia(DefinedTermBase definedTerm) {
348
		checkNotInPriorView("DefinedTermDaoImpl.countMedia(DefinedTermBase definedTerm)");
349
		Query<Long> query = getSession().createQuery("SELECT count(media) "
350
		        + " FROM DefinedTermBase definedTerm JOIN definedTerm.media media "
351
		        + " WHERE definedTerm = :definedTerm",
352
		        Long.class);
353
	    query.setParameter("definedTerm", definedTerm);
354

    
355
		return query.uniqueResult();
356
	}
357

    
358
	@Override
359
    public List<Media> getMedia(DefinedTermBase definedTerm, Integer pageSize,	Integer pageNumber) {
360
		checkNotInPriorView("DefinedTermDaoImpl.getMedia(DefinedTermBase definedTerm, Integer pageSize,	Integer pageNumber)");
361
		Query<Media> query = getSession().createQuery(
362
		           "SELECT media "
363
		        + " FROM DefinedTermBase definedTerm "
364
		        + "   JOIN definedTerm.media media "
365
		        + " WHERE definedTerm = :definedTerm",
366
		        Media.class);
367
		query.setParameter("definedTerm", definedTerm);
368

    
369
		addPageSizeAndNumber(query, pageSize, pageNumber);
370

    
371
        List<Media> result = query.list();
372
		return result;
373
	}
374

    
375
	@Override
376
    public List<NamedArea> list(NamedAreaLevel level, NamedAreaType type, Integer pageSize, Integer pageNumber) {
377
		AuditEvent auditEvent = getAuditEventFromContext();
378
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
379
            Criteria criteria = getCriteria(NamedArea.class);
380

    
381
		    if(level != null) {
382
			    criteria.add(Restrictions.eq("level",level));
383
		    }
384

    
385
		    if(type != null) {
386
			    criteria.add(Restrictions.eq("type", type));
387
		    }
388

    
389
		    addPageSizeAndNumber(criteria, pageSize, pageNumber);
390

    
391
	        @SuppressWarnings("unchecked")
392
	        List<NamedArea> result = deduplicateResult(criteria.list());
393
	        return result;
394
		} else {
395
            AuditQuery query = makeAuditQuery(NamedArea.class, auditEvent);
396

    
397
			if(level != null) {
398
				query.add(AuditEntity.relatedId("level").eq(level.getId()));
399
		    }
400

    
401
		    if(type != null) {
402
		    	query.add(AuditEntity.relatedId("type").eq(type.getId()));
403
		    }
404

    
405
		    @SuppressWarnings("unchecked")
406
            List<NamedArea> result = deduplicateResult(query.getResultList());
407
		    return result;
408
		}
409
	}
410

    
411
	@Override
412
    public List<NamedArea> list(NamedAreaLevel level, NamedAreaType type, Integer pageSize, Integer pageNumber,
413
			List<OrderHint> orderHints, List<String> propertyPaths) {
414

    
415
	    List<NamedArea> result;
416

    
417
		AuditEvent auditEvent = getAuditEventFromContext();
418
		if (auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
419
			Criteria criteria = getCriteria(NamedArea.class);
420

    
421
			if (level != null) {
422
				criteria.add(Restrictions.eq("level", level));
423
			}
424
			if (type != null) {
425
				criteria.add(Restrictions.eq("type", type));
426
			}
427
			addOrder(criteria,orderHints);
428
			addPageSizeAndNumber(criteria, pageSize, pageNumber);
429

    
430
			result = deduplicateResult(criteria.list());
431

    
432
		} else {
433
			AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(NamedArea.class,
434
				auditEvent.getRevisionNumber());
435
			if (level != null) {
436
				query.add(AuditEntity.relatedId("level").eq(level.getId()));
437
			}
438
			if (type != null) {
439
				query.add(AuditEntity.relatedId("type").eq(type.getId()));
440
			}
441
			result = deduplicateResult(query.getResultList());
442
		}
443

    
444
		defaultBeanInitializer.initializeAll(result, propertyPaths);
445

    
446
		return result;
447
	}
448

    
449

    
450
	@Override
451
    public <T extends DefinedTermBase> long countGeneralizationOf(T kindOf) {
452
		AuditEvent auditEvent = getAuditEventFromContext();
453
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
454
		    Query<Long> query = getSession().createQuery(
455
		            "   SELECT count(term) "
456
		            + " FROM DefinedTermBase term "
457
		            + " WHERE term.kindOf = :kindOf",
458
		            Long.class);
459
		    query.setParameter("kindOf", kindOf);
460
		    return query.uniqueResult();
461
		} else {
462
            AuditQuery query = makeAuditQuery(DefinedTermBase.class,auditEvent);
463
			query.add(AuditEntity.relatedId("kindOf").eq(kindOf.getId()));
464
		    query.addProjection(AuditEntity.id().count());
465
		    return (Long)query.getSingleResult();
466
		}
467
	}
468

    
469
	@Override
470
    public <T extends DefinedTermBase> long countIncludes(Collection<T> partOf) {
471
		if (partOf == null || partOf.isEmpty()){
472
			return 0;
473
		}
474
		AuditEvent auditEvent = getAuditEventFromContext();
475
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
476
    		Query<Long> query = getSession().createQuery(
477
    		        "   SELECT count(term) "
478
    		        + " FROM DefinedTermBase term "
479
    		        + " WHERE term.partOf in (:partOf)",
480
    		        Long.class);
481
	    	query.setParameterList("partOf", partOf);
482
		    return query.uniqueResult();
483
		} else {
484
			long count = 0;
485
			for(T t : partOf) {
486
				AuditQuery query = makeAuditQuery(DefinedTermBase.class, auditEvent);
487
				query.add(AuditEntity.relatedId("partOf").eq(t.getId()));
488
			    query.addProjection(AuditEntity.id().count());
489
			    count += (Long)query.getSingleResult();
490
			}
491
			return count;
492
		}
493
	}
494

    
495
	@Override
496
    public <T extends DefinedTermBase> List<T> getGeneralizationOf(T kindOf, Integer pageSize, Integer pageNumber) {
497
		AuditEvent auditEvent = getAuditEventFromContext();
498
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
499
            Query<DefinedTermBase> query = getSession().createQuery("SELECT term FROM DefinedTermBase term WHERE term.kindOf = :kindOf", DefinedTermBase.class);
500
		    query.setParameter("kindOf", kindOf);
501

    
502
		    addPageSizeAndNumber(query, pageSize, pageNumber);
503
            return (List)deduplicateResult(query.list());
504
		} else {
505
			 AuditQuery query = makeAuditQuery(DefinedTermBase.class, auditEvent);
506
			 query.add(AuditEntity.relatedId("kindOf").eq(kindOf.getId()));
507

    
508
			 addPageSizeAndNumber(query, pageSize, pageNumber);
509

    
510
             @SuppressWarnings("unchecked")
511
             List<T> result = deduplicateResult(query.getResultList());
512
             return result;
513
		}
514
	}
515

    
516
	@Override
517
    public <T extends DefinedTermBase> List<T> getIncludes(Collection<T> partOf,	Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
518
		if (partOf == null || partOf.isEmpty()){
519
			return new ArrayList<>();
520
		}
521
		AuditEvent auditEvent = getAuditEventFromContext();
522
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
523
            Query<DefinedTermBase> query = getSession().createQuery("SELECT term FROM DefinedTermBase term WHERE term.partOf in (:partOf)", DefinedTermBase.class);
524
    		query.setParameterList("partOf", partOf);
525

    
526
    		addPageSizeAndNumber(query, pageSize, pageNumber);
527

    
528
            List<T> results = (List)deduplicateResult(query.list());
529
		    defaultBeanInitializer.initializeAll(results, propertyPaths);
530
		    return results;
531
		} else {
532
			List<T> result = new ArrayList<>();
533
			for(T t : partOf) {
534
				AuditQuery query = makeAuditQuery(DefinedTermBase.class, auditEvent);
535
				query.add(AuditEntity.relatedId("partOf").eq(t.getId()));
536
				addPageSizeAndNumber(query, pageSize, pageNumber);
537

    
538
			    result.addAll(deduplicateResult(query.getResultList()));
539
			}
540
			defaultBeanInitializer.initializeAll(result, propertyPaths);
541
			return result;
542
		}
543
	}
544

    
545
	@Override
546
    public <T extends DefinedTermBase> long countPartOf(Set<T> definedTerms) {
547
		checkNotInPriorView("DefinedTermDaoImpl.countPartOf(Set<T> definedTerms)");
548
		Query<Long> query = getSession().createQuery("SELECT count(DISTINCT definedTerm) FROM DefinedTermBase definedTerm JOIN definedTerm.includes included WHERE included in (:definedTerms)", Long.class);
549
		query.setParameterList("definedTerms", definedTerms);
550
		return query.uniqueResult();
551
	}
552

    
553
	@Override
554
    public <T extends DefinedTermBase> List<T> getPartOf(Set<T> definedTerms, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
555
		checkNotInPriorView("DefinedTermDaoImpl.getPartOf(Set<T> definedTerms, Integer pageSize, Integer pageNumber)");
556
		@SuppressWarnings("unchecked")
557
        Query<T> query = getSession().createQuery("select distinct definedTerm from DefinedTermBase definedTerm join definedTerm.includes included where included in (:definedTerms)");
558
		query.setParameterList("definedTerms", definedTerms);
559

    
560
		addPageSizeAndNumber(query, pageSize, pageNumber);
561

    
562
        List<T> r = query.list();
563
		/**
564
		 * For some weird reason, hibernate returns proxies (extending the superclass), not the actual class on this,
565
		 * despite querying the damn database and returning the discriminator along with the rest of the object properties!
566
		 *
567
		 * Probably a bug in hibernate, but we'll manually deproxy for now since the objects are initialized anyway, the
568
		 * performance implications are small (we're swapping one array of references for another, not hitting the db or
569
		 * cache).
570
		 */
571
		List<T> results = new ArrayList<>();
572
		if(!definedTerms.isEmpty()) {
573
		    for(T t : r) {
574
		        T deproxied = CdmBase.deproxy(t);
575
                results.add(deproxied);
576
		    }
577
		    defaultBeanInitializer.initializeAll(results, propertyPaths);
578
		}
579
		return results;
580
	}
581

    
582
	@Override
583
    public DefinedTermBase findByUri(URI uri) {
584
		AuditEvent auditEvent = getAuditEventFromContext();
585
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
586
		    Query<DefinedTermBase> query = getSession().createQuery("select term from DefinedTermBase term where term.uri = :uri", DefinedTermBase.class);
587
		    query.setParameter("uri", uri);
588
		    return query.uniqueResult();
589
		} else {
590
			AuditQuery query = makeAuditQuery(DefinedTermBase.class, auditEvent);
591
			query.add(AuditEntity.property("uri").eq(uri));
592
		    return (DefinedTermBase<?>)query.getSingleResult();
593
		}
594
	}
595

    
596
	@Override
597
	public <T extends DefinedTermBase> List<T> listByTermType(TermType termType, Integer limit, Integer start,
598
	        List<OrderHint> orderHints, List<String> propertyPaths) {
599
	    @SuppressWarnings("unchecked")
600
        Query<T> query = getSession().createQuery("SELECT term FROM DefinedTermBase term WHERE term.termType = :termType");
601
	    query.setParameter("termType", termType);
602

    
603
        List<T> result = deduplicateResult(query.list());
604

    
605
	    defaultBeanInitializer.initializeAll(result, propertyPaths);
606
        return result;
607
	}
608

    
609
	@Override
610
    public <TERM extends DefinedTermBase> List<TERM> listByTermClass(Class<TERM> clazz, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
611
//		checkNotInPriorView("DefinedTermDaoImpl.listByTermClass(Class<TERM> clazz, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths)");
612

    
613
        Query<TERM> query = getSession().createQuery("FROM " + clazz.getSimpleName(), clazz);
614
        List<TERM> result = deduplicateResult(query.list());
615

    
616
	    defaultBeanInitializer.initializeAll(result, propertyPaths);
617
	    return result;
618
	}
619

    
620
    @Override
621
    public <S extends DefinedTermBase> List<S> list(Class<S> type, Integer limit, Integer start,
622
            List<OrderHint> orderHints, List<String> propertyPath) {
623

    
624
        return deduplicateResult(super.list(type, limit, start, orderHints, propertyPath));
625
    }
626

    
627
    /**
628
     * Workaround for https://dev.e-taxonomy.eu/redmine/issues/5871 and #5945
629
     * Terms with multiple representations return identical duplicates
630
     * due to eager representation loading. We expect these duplicates to appear
631
     * in line wo we only compare one term with its predecessor. If it already
632
     * exists we remove it from the result.
633
     * @param orginals
634
     * @return
635
     */
636
    protected static <S extends CdmBase> List<S> deduplicateResult(List<S> orginals) {
637
        List<S> result = new ArrayList<>();
638
        Iterator<S> it = orginals.iterator();
639
        S last = null;
640
        while (it.hasNext()){
641
            S a = it.next();
642
            if (a != last){
643
                //AM: why is this necessary?
644
                if (!result.contains(a)){
645
                    result.add(a);
646
                }
647
            }
648
            last = a;
649
        }
650
        return result;
651
    }
652

    
653
    @Override
654
    public <S extends DefinedTermBase> List<S> list(Class<S> clazz, List<TermVocabulary> vocs, Integer limit, String pattern) {
655
        return list(clazz, vocs, 0, limit, pattern, MatchMode.BEGINNING);
656
    }
657

    
658
    @Override
659
    public <S extends DefinedTermBase> List<S> list(Class<S> clazz, List<TermVocabulary> vocs, Integer pageNumber, Integer limit, String pattern, MatchMode matchmode){
660
        if (clazz == null){
661
            clazz = (Class)type;
662
        }
663
        Criteria crit = getSession().createCriteria(clazz, "term");
664
        if (!StringUtils.isBlank(pattern)){
665
            crit.createAlias("term.representations", "reps");
666
            Disjunction or = Restrictions.disjunction();
667
            if (matchmode == MatchMode.EXACT) {
668
                or.add(Restrictions.eq("titleCache", matchmode.queryStringFrom(pattern)));
669
                or.add(Restrictions.eq("reps.label", matchmode.queryStringFrom(pattern)));
670
            } else {
671
                or.add(Restrictions.like("titleCache", matchmode.queryStringFrom(pattern)));
672
                or.add(Restrictions.like("reps.label", matchmode.queryStringFrom(pattern)));
673
            }
674
            crit.add(or);
675
        }
676

    
677
        if (limit != null && limit >= 0) {
678
            crit.setMaxResults(limit);
679
        }
680

    
681
        if (vocs != null &&!vocs.isEmpty()){
682
            crit.createAlias("term.vocabulary", "voc");
683
            Disjunction or = Restrictions.disjunction();
684
            for (TermVocabulary<?> voc: vocs){
685
                Criterion criterion = Restrictions.eq("voc.id", voc.getId());
686
                or.add(criterion);
687
            }
688
            crit.add(or);
689
        }
690

    
691
        crit.addOrder(Order.asc("titleCache"));
692
        if (limit == null){
693
            limit = 1;
694
        }
695
        crit.setFirstResult(0);
696
        @SuppressWarnings("unchecked")
697
        List<S> results = deduplicateResult(crit.list());
698
        return results;
699
    }
700

    
701
    @Override
702
    public <S extends DefinedTermBase> List<S> listByAbbrev(Class<S> clazz, List<TermVocabulary> vocs, Integer limit, String pattern, TermSearchField type) {
703
        return listByAbbrev(clazz, vocs, 0, limit, pattern, MatchMode.BEGINNING, type);
704
    }
705

    
706
    @Override
707
    public <S extends DefinedTermBase> List<S> listByAbbrev(Class<S> clazz, List<TermVocabulary> vocs, Integer pageNumber, Integer limit, String pattern, MatchMode matchmode, TermSearchField abbrevType){
708

    
709
        if (clazz == null){
710
            clazz = (Class)type;
711
        }
712
        Criteria crit = getSession().createCriteria(clazz, "type");
713
        if (!StringUtils.isBlank(pattern)){
714
            if (matchmode == MatchMode.EXACT) {
715
                crit.add(Restrictions.eq(abbrevType.getKey(), matchmode.queryStringFrom(pattern)));
716
            } else {
717
                crit.add(Restrictions.like(abbrevType.getKey(), matchmode.queryStringFrom(pattern)));
718
            }
719
        }
720
        if (limit != null && limit >= 0) {
721
            crit.setMaxResults(limit);
722
        }
723

    
724
        if (vocs != null &&!vocs.isEmpty()){
725
            crit.createAlias("type.vocabulary", "voc");
726
            Disjunction or = Restrictions.disjunction();
727
            for (TermVocabulary<?> voc: vocs){
728
                Criterion criterion = Restrictions.eq("voc.id", voc.getId());
729
                or.add(criterion);
730
            }
731
            crit.add(or);
732
        }
733

    
734
        crit.addOrder(Order.asc(abbrevType.getKey()));
735
        if (limit == null){
736
            limit = 1;
737
        }
738
//        int firstItem = (pageNumber - 1) * limit;
739

    
740
        crit.setFirstResult(0);
741
        @SuppressWarnings("unchecked")
742
        List<S> results = deduplicateResult(crit.list());
743
        return results;
744
    }
745

    
746

    
747
    @Override
748
    public Collection<TermDto> getIncludesAsDto(
749
            TermDto parentTerm) {
750
        String queryString;
751
        if (parentTerm.getTermType().equals(TermType.NamedArea)){
752
            queryString = TermDto.getTermDtoSelectNamedArea();
753
        }else{
754
            queryString = TermDto.getTermDtoSelect();
755
        }
756
        queryString = queryString
757
                + "where a.partOf.uuid = :parentUuid";
758
        Query<Object[]> query =  getSession().createQuery(queryString, Object[].class);
759
        query.setParameter("parentUuid", parentTerm.getUuid());
760

    
761
        List<Object[]> result = query.list();
762

    
763
        List<TermDto> list = TermDto.termDtoListFrom(result);
764
        return list;
765
    }
766

    
767
    @Override
768
    public Collection<TermDto> getKindOfsAsDto(TermDto parentTerm) {
769

    
770
        String queryString;
771
        if (parentTerm.getTermType().equals(TermType.NamedArea)){
772
            queryString = TermDto.getTermDtoSelectNamedArea();
773
        }else{
774
            queryString = TermDto.getTermDtoSelect();
775
        }
776
        queryString = queryString + "where a.kindOf.uuid = :parentUuid";
777
        Query<Object[]> query =  getSession().createQuery(queryString, Object[].class);
778
        query.setParameter("parentUuid", parentTerm.getUuid());
779

    
780
        List<Object[]> result = query.list();
781

    
782
        List<TermDto> list = TermDto.termDtoListFrom(result);
783
        return list;
784
    }
785

    
786
    @Override
787
    public Collection<TermDto> findByTitleAsDtoWithVocDto(String title, TermType termType) {
788

    
789
        //terms
790
        String termQueryString = TermDto.getTermDtoSelect()
791
                + " where a.titleCache like :title "
792
                + (termType!=null?" and a.termType = :termType ":"");
793

    
794
        title = title.replace("*", "%");
795
        Query<Object[]> termQuery = getSession().createQuery(termQueryString, Object[].class);
796
        termQuery.setParameter("title", "%"+title+"%");
797
        if(termType!=null){
798
            termQuery.setParameter("termType", termType);
799
        }
800

    
801
        List<Object[]> termArrayResult = termQuery.list();
802
        List<TermDto> list = TermDto.termDtoListFrom(termArrayResult);
803

    
804
        //vocabularies
805
        String vocQueryString = TermCollectionDto.getTermCollectionDtoSelect() + " WHERE a.uuid = :uuid";
806
        Query<Object[]> vocQuery = getSession().createQuery(vocQueryString, Object[].class);
807
        Map<UUID,TermVocabularyDto> vocMap = new HashMap<>();
808
        for (TermDto dto: list){
809
            UUID vocUuid = dto.getVocabularyUuid();
810
            TermVocabularyDto vocDto = vocMap.get(vocUuid);
811
            if (vocDto == null){
812
                vocQuery.setParameter("uuid", dto.getVocabularyUuid());
813
                List<Object[]> vocArrayResult = vocQuery.list();
814
                List<TermVocabularyDto> vocs = TermVocabularyDto.termVocabularyDtoListFrom(vocArrayResult);
815
                if (!vocs.isEmpty()){
816
                    vocDto = vocs.get(0);
817
                    vocMap.put(vocUuid, vocs.get(0));
818
                }
819
            }
820
            dto.setVocabularyDto(vocDto);
821
        }
822
        return list;
823
    }
824

    
825
    @Override
826
    public TermDto findByUUIDAsDto(UUID uuid) {
827

    
828
        String queryString = TermDto.getTermDtoSelect()
829
                + " where a.uuid like :uuid ";
830
        Query<Object[]> query =  getSession().createQuery(queryString, Object[].class);
831
        query.setParameter("uuid", uuid);
832

    
833
        List<Object[]> result = query.list();
834

    
835
        List<TermDto> list = TermDto.termDtoListFrom(result);
836
        if (list.size()== 1){
837
            return list.get(0);
838
        }else{
839
            return null;
840
        }
841
    }
842

    
843
    @Override
844
    public Collection<TermDto> findByTypeAsDto(TermType termType) {
845
        if (termType == null){
846
            return null;
847
        }
848
        String queryString = TermDto.getTermDtoSelect()
849
                + " WHERE a.termType = :termType ";
850
        Query<Object[]> query =  getSession().createQuery(queryString, Object[].class);
851

    
852
        query.setParameter("termType", termType);
853

    
854
        List<Object[]> result = query.list();
855
        List<TermDto> list = TermDto.termDtoListFrom(result);
856
        return list;
857
    }
858

    
859
    @Override
860
    public Collection<TermDto> findByUriAsDto(URI uri, String termLabel, TermType termType) {
861
        String queryString = TermDto.getTermDtoSelect()
862
                + " where a.uri like :uri "
863
                + (termType!=null?" and a.termType = :termType ":"")
864
                + (termLabel!=null?" and a.titleCache = :termLabel ":"")
865
                ;
866
        Query<Object[]> query =  getSession().createQuery(queryString, Object[].class);
867
        query.setParameter("uri", uri.toString());
868
        if(termLabel!=null){
869
            query.setParameter("termLabel", "%"+termLabel+"%");
870
        }
871
        if(termType!=null){
872
            query.setParameter("termType", termType);
873
        }
874

    
875
        List<Object[]> result = query.list();
876

    
877
        List<TermDto> list = TermDto.termDtoListFrom(result);
878
        return list;
879
    }
880

    
881
    @Override
882
    public  Map<UUID, List<TermDto>> getSupportedStatesForFeature(Set<UUID> featureUuids){
883
        Map<UUID, List<TermDto>> map = new HashMap<>();
884
        for (UUID featureUuid: featureUuids){
885
            List<TermDto> list = new ArrayList<>();
886
            String supportedCategoriesQueryString = "SELECT cat.uuid "
887
                    + "from DefinedTermBase t "
888
                    + "join t.supportedCategoricalEnumerations as cat "
889
                    + "where t.uuid = :featureUuid";
890
            Query<UUID> supportedCategoriesQuery =  getSession().createQuery(supportedCategoriesQueryString, UUID.class);
891
            supportedCategoriesQuery.setParameter("featureUuid", featureUuid);
892
    		List<UUID> supportedCategories = supportedCategoriesQuery.list();
893
            if(supportedCategories.isEmpty()){
894
                map.put(featureUuid, list);
895
                continue;
896
            }
897

    
898
            String queryString = TermDto.getTermDtoSelect()
899
                    + "where v.uuid in (:supportedCategories) "
900
                    + "order by a.titleCache";
901
            Query<Object[]> query =  getSession().createQuery(queryString, Object[].class);
902
            query.setParameterList("supportedCategories", supportedCategories);
903

    
904
            List<Object[]> result = query.list();
905
            list = TermDto.termDtoListFrom(result);
906
            map.put(featureUuid, list);
907
        }
908
        return map;
909
    }
910

    
911
    @Override
912
    public Collection<TermDto> findByUUIDsAsDto(List<UUID> uuidList) {
913
        List<TermDto> list = new ArrayList<>();
914
        if (uuidList == null || uuidList.isEmpty()){
915
            return null;
916
        }
917

    
918
        String queryString = TermDto.getTermDtoSelect()
919
                + " WHERE a.uuid in :uuidList "
920
                + " ORDER by a.titleCache";
921
        Query<Object[]> query =  getSession().createQuery(queryString, Object[].class);
922
        query.setParameterList("uuidList", uuidList);
923

    
924
        List<Object[]> result = query.list();
925
        list = TermDto.termDtoListFrom(result);
926
        return list;
927
    }
928

    
929
    @Override
930
    public Collection<TermDto> findFeatureByUUIDsAsDto(List<UUID> uuidList) {
931
        List<TermDto> list = new ArrayList<>();
932
        if (uuidList == null || uuidList.isEmpty()){
933
            return null;
934
        }
935

    
936
        String queryString = FeatureDto.getTermDtoSelect()
937
                + "where a.uuid in :uuidList "
938
                + "order by a.titleCache";
939
        Query<Object[]> query =  getSession().createQuery(queryString, Object[].class);
940
        query.setParameterList("uuidList", uuidList);
941

    
942
        List<Object[]> result = query.list();
943

    
944
        list = FeatureDto.termDtoListFrom(result);
945
        return list;
946
    }
947

    
948
    @Override
949
    public Collection<TermDto> findFeatureByTitleAsDto(String pattern) {
950
        String queryString = FeatureDto.getTermDtoSelect()
951
                + " where a.titleCache like :title "
952
                +  " and a.termType = :termType ";
953

    
954
        pattern = pattern.replace("*", "%");
955
        Query<Object[]> query =  getSession().createQuery(queryString, Object[].class);
956
        query.setParameter("title", "%"+pattern+"%");
957
        query.setParameter("termType", TermType.Feature);
958

    
959
        List<Object[]> result = query.list();
960
        List<TermDto> list = FeatureDto.termDtoListFrom(result);
961
        return list;
962
    }
963

    
964
    @Override
965
    public TermDto getTermDto(UUID uuid) {
966
        String queryString = TermDto.getTermDtoSelect()
967
                + " where a.uuid = :uuid ";
968

    
969
        Query<Object[]> query =  getSession().createQuery(queryString, Object[].class);
970
        query.setParameter("uuid", uuid);
971

    
972
        List<Object[]> result = query.list();
973
        TermDto dto = null;
974
        List<TermDto> dtoList = TermDto.termDtoListFrom(result);
975
        if (dtoList != null && !dtoList.isEmpty()){
976
            dto = dtoList.get(0);
977
        }
978
        return dto;
979
    }
980

    
981
  //***************** Overrides for deduplication *******************************/
982

    
983
    @Override
984
    public List<DefinedTermBase> loadList(Collection<Integer> ids, List<OrderHint> orderHints,
985
            List<String> propertyPaths) throws DataAccessException {
986
        return DefinedTermDaoImpl.deduplicateResult(super.loadList(ids, orderHints, propertyPaths));
987
    }
988

    
989
    @Override
990
    public List<DefinedTermBase> list(Collection<UUID> uuids, Integer pageSize, Integer pageNumber,
991
            List<OrderHint> orderHints, List<String> propertyPaths) throws DataAccessException {
992
        return DefinedTermDaoImpl.deduplicateResult(super.list(uuids, pageSize, pageNumber, orderHints, propertyPaths));
993
    }
994

    
995
    @Override
996
    public <S extends DefinedTermBase> List<S> list(Class<S> clazz, Collection<UUID> uuids, Integer pageSize,
997
            Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) throws DataAccessException {
998
        return DefinedTermDaoImpl.deduplicateResult(super.list(clazz, uuids, pageSize, pageNumber, orderHints, propertyPaths));
999
    }
1000

    
1001
    @Override
1002
    public <S extends DefinedTermBase> List<S> list(Class<S> type, List<Restriction<?>> restrictions, Integer limit,
1003
            Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
1004
        return DefinedTermDaoImpl.deduplicateResult(super.list(type, restrictions, limit, start, orderHints, propertyPaths));
1005
    }
1006

    
1007
    @Override
1008
    public List<DefinedTermBase> list(Integer limit, Integer start, List<OrderHint> orderHints) {
1009
        return DefinedTermDaoImpl.deduplicateResult(super.list(limit, start, orderHints));
1010
    }
1011

    
1012
    @Override
1013
    public List<DefinedTermBase> list(Integer limit, Integer start, List<OrderHint> orderHints,
1014
            List<String> propertyPaths) {
1015
        return DefinedTermDaoImpl.deduplicateResult(super.list(limit, start, orderHints, propertyPaths));
1016
    }
1017

    
1018
    @Override
1019
    public <S extends DefinedTermBase> List<S> list(Class<S> type, Integer limit, Integer start,
1020
            List<OrderHint> orderHints) {
1021
        return DefinedTermDaoImpl.deduplicateResult(super.list(type, limit, start, orderHints));
1022
    }
1023
}
(1-1/6)