Project

General

Profile

Download (35.8 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.log4j.Logger;
24
import org.hibernate.Criteria;
25
import org.hibernate.Query;
26
import org.hibernate.Session;
27
import org.hibernate.criterion.Criterion;
28
import org.hibernate.criterion.Disjunction;
29
import org.hibernate.criterion.Order;
30
import org.hibernate.criterion.Projections;
31
import org.hibernate.criterion.Restrictions;
32
import org.hibernate.envers.query.AuditEntity;
33
import org.hibernate.envers.query.AuditQuery;
34
import org.springframework.stereotype.Repository;
35

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

    
76
/**
77
 * @author a.kohlbecker
78
 * @since 29.05.2008
79
 */
80
@Repository
81
public class DefinedTermDaoImpl
82
        extends IdentifiableDaoBase<DefinedTermBase>
83
        implements IDefinedTermDao{
84

    
85
    private static final Logger logger = Logger.getLogger(DefinedTermDaoImpl.class);
86

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

    
118
	/**
119
	 * Searches by Label
120
	 */
121
	@Override
122
    public List<DefinedTermBase> findByTitle(String queryString) {
123
		return findByTitle(queryString, null);
124
	}
125

    
126
	/**
127
	 * Searches by Label
128
	 */
129
	@Override
130
    public List<DefinedTermBase> findByTitle(String queryString, CdmBase sessionObject) {
131
		checkNotInPriorView("DefinedTermDaoImpl.findByTitle(String queryString, CdmBase sessionObject)");
132
		Session session = getSession();
133
		if ( sessionObject != null ) {//attache the object to the session, TODO needed?
134
			session.update(sessionObject);
135
		}
136
		Query query = session.createQuery("select term from DefinedTermBase term join fetch term.representations representation where representation.label = :label");
137
		query.setParameter("label", queryString);
138
		@SuppressWarnings({ "unchecked", "rawtypes" })
139
		List<DefinedTermBase> result = deduplicateResult(query.list());
140
		return result;
141
	}
142

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

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

    
174
	@Override
175
    public <T extends DefinedTermBase> List<T> getDefinedTermByRepresentationText(String text, Class<T> clazz ) {
176
		return getDefinedTermByRepresentationText(text,clazz,null,null);
177
	}
178

    
179
	@Override
180
    public <T extends DefinedTermBase> List<T> getDefinedTermByRepresentationText(String text, Class<T> clazz, Integer pageSize,Integer  pageNumber) {
181
		checkNotInPriorView("DefinedTermDaoImpl.getDefinedTermByRepresentationText(String text, Class<T> clazz, Integer pageSize,Integer  pageNumber)");
182

    
183
		Criteria criteria = getCriteria(clazz);
184

    
185
		criteria.createAlias("representations", "r").add(Restrictions.like("r.text", text));
186

    
187
		addPageSizeAndNumber(criteria, pageSize, pageNumber);
188

    
189
		@SuppressWarnings("unchecked")
190
        List<T> result = deduplicateResult(criteria.list());
191
		return result;
192
	}
193

    
194
	@Override
195
    public long countDefinedTermByRepresentationText(String text, Class<? extends DefinedTermBase> clazz) {
196
	    checkNotInPriorView("DefinedTermDaoImpl.countDefinedTermByRepresentationText(String text, Class<? extends DefinedTermBase> clazz)");
197
		Criteria criteria = getCriteria(clazz);
198

    
199
		criteria.createAlias("representations", "r").add(Restrictions.like("r.text", text));
200

    
201
		criteria.setProjection(Projections.rowCount());
202

    
203
		return (Long)criteria.uniqueResult();
204
	}
205

    
206
	@Override
207
	public <T extends DefinedTermBase> List<T> getDefinedTermByIdInVocabulary(String label, UUID vocUuid, Class<T> clazz, Integer pageSize, Integer pageNumber) {
208
		checkNotInPriorView("DefinedTermDaoImpl.getDefinedTermByIdInVocabulary(String label, UUID vocUuid, Class<T> clazz, Integer pageSize, Integer pageNumber)");
209

    
210
		Criteria criteria = getCriteria(clazz);
211

    
212
		criteria.createAlias("vocabulary", "voc")
213
		    .add(Restrictions.like("voc.uuid", vocUuid))
214
			.add(Restrictions.like("idInVocabulary", label, org.hibernate.criterion.MatchMode.EXACT));
215

    
216
		addPageSizeAndNumber(criteria, pageSize, pageNumber);
217

    
218
		@SuppressWarnings("unchecked")
219
        List<T> result = deduplicateResult(criteria.list());
220
		return result;
221
	}
222

    
223
    @Override
224
	public <T extends DefinedTermBase> List<T> getDefinedTermByRepresentationAbbrev(String text, Class<T> clazz, Integer pageSize,Integer  pageNumber) {
225
		checkNotInPriorView("DefinedTermDaoImpl.getDefinedTermByRepresentationAbbrev(String abbrev, Class<T> clazz, Integer pageSize,Integer  pageNumber)");
226

    
227
		Criteria criteria = getCriteria(clazz);
228

    
229
		criteria.createAlias("representations", "r").add(Restrictions.like("r.abbreviatedLabel", text));
230

    
231
		addPageSizeAndNumber(criteria, pageSize, pageNumber);
232

    
233
		@SuppressWarnings("unchecked")
234
		List<T> result = deduplicateResult(criteria.list());
235
		return result;
236
	}
237

    
238
	@Override
239
	public long countDefinedTermByRepresentationAbbrev(String text, Class<? extends DefinedTermBase> clazz) {
240
	    checkNotInPriorView("DefinedTermDaoImpl.countDefinedTermByRepresentationAbbrev(String abbrev, Class<? extends DefinedTermBase> clazz)");
241
		Criteria criteria = getCriteria(clazz);
242

    
243
		criteria.createAlias("representations", "r").add(Restrictions.like("r.abbreviatedLabel", text));
244
		criteria.setProjection(Projections.rowCount());
245

    
246
        return (Long)criteria.uniqueResult();
247
	}
248

    
249
	@Override
250
    public Language getLanguageByIso(String iso639) {
251
		if (iso639.length() < 2 || iso639.length() > 3) {
252
			logger.warn("Invalid length " + iso639.length() + " of ISO code. Length must be 2 or 3.");
253
			return null;
254
		}
255
		boolean isIso639_1 = iso639.length() == 2;
256

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

    
280
			return (Language)query.getSingleResult();
281
		}
282
	}
283

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

    
297
	@Override
298
    public List<Language> getLanguagesByLocale(Enumeration<Locale> locales) {
299
		List<Language> languages = new ArrayList<>();
300
		while(locales.hasMoreElements()) {
301
			Locale locale = locales.nextElement();
302
			languages.add(getLanguageByIso(locale.getLanguage()));
303
		}
304
		return languages;
305
	}
306

    
307
	@Override
308
    public long count(NamedAreaLevel level, NamedAreaType type) {
309
		AuditEvent auditEvent = getAuditEventFromContext();
310
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
311
		    Criteria criteria = getCriteria(NamedArea.class);
312

    
313
		    if(level != null) {
314
			    criteria.add(Restrictions.eq("level",level));
315
		    }
316

    
317
		    if(type != null) {
318
			    criteria.add(Restrictions.eq("type", type));
319
		    }
320

    
321
		    criteria.setProjection(Projections.rowCount());
322

    
323
	        return (Long)criteria.uniqueResult();
324
		} else {
325
			AuditQuery query = makeAuditQuery(NamedArea.class, auditEvent);
326

    
327
			if(level != null) {
328
				query.add(AuditEntity.relatedId("level").eq(level.getId()));
329
		    }
330

    
331
		    if(type != null) {
332
		    	query.add(AuditEntity.relatedId("type").eq(type.getId()));
333
		    }
334
		    query.addProjection(AuditEntity.id().count());
335
		    return (Long)query.getSingleResult();
336
		}
337
	}
338

    
339
	@Override
340
    public long countMedia(DefinedTermBase definedTerm) {
341
		checkNotInPriorView("DefinedTermDaoImpl.countMedia(DefinedTermBase definedTerm)");
342
		Query query = getSession().createQuery("select count(media) from DefinedTermBase definedTerm join definedTerm.media media where definedTerm = :definedTerm");
343
	    query.setParameter("definedTerm", definedTerm);
344

    
345
		return (Long)query.uniqueResult();
346
	}
347

    
348
	@Override
349
    public List<Media> getMedia(DefinedTermBase definedTerm, Integer pageSize,	Integer pageNumber) {
350
		checkNotInPriorView("DefinedTermDaoImpl.getMedia(DefinedTermBase definedTerm, Integer pageSize,	Integer pageNumber)");
351
		Query query = getSession().createQuery(
352
		           "SELECT media "
353
		        + " FROM DefinedTermBase definedTerm "
354
		        + " JOIN definedTerm.media media "
355
		        + " WHERE definedTerm = :definedTerm");
356
		query.setParameter("definedTerm", definedTerm);
357

    
358
		addPageSizeAndNumber(query, pageSize, pageNumber);
359

    
360
		@SuppressWarnings("unchecked")
361
        List<Media> result = query.list();
362
		return result;
363
	}
364

    
365
	@Override
366
    public List<NamedArea> list(NamedAreaLevel level, NamedAreaType type, Integer pageSize, Integer pageNumber) {
367
		AuditEvent auditEvent = getAuditEventFromContext();
368
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
369
            Criteria criteria = getCriteria(NamedArea.class);
370

    
371
		    if(level != null) {
372
			    criteria.add(Restrictions.eq("level",level));
373
		    }
374

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

    
379
		    addPageSizeAndNumber(criteria, pageSize, pageNumber);
380

    
381
	        @SuppressWarnings("unchecked")
382
	        List<NamedArea> result = deduplicateResult(criteria.list());
383
	        return result;
384
		} else {
385
            AuditQuery query = makeAuditQuery(NamedArea.class, auditEvent);
386

    
387
			if(level != null) {
388
				query.add(AuditEntity.relatedId("level").eq(level.getId()));
389
		    }
390

    
391
		    if(type != null) {
392
		    	query.add(AuditEntity.relatedId("type").eq(type.getId()));
393
		    }
394

    
395
		    @SuppressWarnings("unchecked")
396
            List<NamedArea> result = deduplicateResult(query.getResultList());
397
		    return result;
398
		}
399
	}
400

    
401
	@Override
402
    public List<NamedArea> list(NamedAreaLevel level, NamedAreaType type, Integer pageSize, Integer pageNumber,
403
			List<OrderHint> orderHints, List<String> propertyPaths) {
404

    
405
	    List<NamedArea> result;
406

    
407
		AuditEvent auditEvent = getAuditEventFromContext();
408
		if (auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
409
			Criteria criteria = getCriteria(NamedArea.class);
410

    
411
			if (level != null) {
412
				criteria.add(Restrictions.eq("level", level));
413
			}
414
			if (type != null) {
415
				criteria.add(Restrictions.eq("type", type));
416
			}
417
			addOrder(criteria,orderHints);
418
			addPageSizeAndNumber(criteria, pageSize, pageNumber);
419

    
420
			result = deduplicateResult(criteria.list());
421

    
422
		} else {
423
			AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision(NamedArea.class,
424
				auditEvent.getRevisionNumber());
425
			if (level != null) {
426
				query.add(AuditEntity.relatedId("level").eq(level.getId()));
427
			}
428
			if (type != null) {
429
				query.add(AuditEntity.relatedId("type").eq(type.getId()));
430
			}
431
			result = deduplicateResult(query.getResultList());
432
		}
433

    
434
		defaultBeanInitializer.initializeAll(result, propertyPaths);
435

    
436
		return result;
437
	}
438

    
439

    
440
	@Override
441
    public <T extends DefinedTermBase> long countGeneralizationOf(T kindOf) {
442
		AuditEvent auditEvent = getAuditEventFromContext();
443
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
444
		    Query query = getSession().createQuery("select count(term) from DefinedTermBase term where term.kindOf = :kindOf");
445
		    query.setParameter("kindOf", kindOf);
446
		    return (Long)query.uniqueResult();
447
		} else {
448
            AuditQuery query = makeAuditQuery(DefinedTermBase.class,auditEvent);
449
			query.add(AuditEntity.relatedId("kindOf").eq(kindOf.getId()));
450
		    query.addProjection(AuditEntity.id().count());
451
		    return (Long)query.getSingleResult();
452
		}
453
	}
454

    
455
	@Override
456
    public <T extends DefinedTermBase> long countIncludes(Collection<T> partOf) {
457
		if (partOf == null || partOf.isEmpty()){
458
			return 0;
459
		}
460
		AuditEvent auditEvent = getAuditEventFromContext();
461
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
462
    		Query query = getSession().createQuery("select count(term) from DefinedTermBase term where term.partOf in (:partOf)");
463
	    	query.setParameterList("partOf", partOf);
464
		    return (Long)query.uniqueResult();
465
		} else {
466
			long count = 0;
467
			for(T t : partOf) {
468
				AuditQuery query = makeAuditQuery(DefinedTermBase.class, auditEvent);
469
				query.add(AuditEntity.relatedId("partOf").eq(t.getId()));
470
			    query.addProjection(AuditEntity.id().count());
471
			    count += (Long)query.getSingleResult();
472
			}
473
			return count;
474
		}
475
	}
476

    
477
	@Override
478
    public <T extends DefinedTermBase> List<T> getGeneralizationOf(T kindOf, Integer pageSize, Integer pageNumber) {
479
		AuditEvent auditEvent = getAuditEventFromContext();
480
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
481
		    Query query = getSession().createQuery("select term from DefinedTermBase term where term.kindOf = :kindOf");
482
		    query.setParameter("kindOf", kindOf);
483

    
484
		    addPageSizeAndNumber(query, pageSize, pageNumber);
485

    
486
		    @SuppressWarnings("unchecked")
487
            List<T> result = deduplicateResult(query.list());
488
		    return result;
489
		} else {
490
			 AuditQuery query = makeAuditQuery(DefinedTermBase.class, auditEvent);
491
			 query.add(AuditEntity.relatedId("kindOf").eq(kindOf.getId()));
492

    
493
			 addPageSizeAndNumber(query, pageSize, pageNumber);
494

    
495
             @SuppressWarnings("unchecked")
496
             List<T> result = deduplicateResult(query.getResultList());
497
             return result;
498
		}
499
	}
500

    
501
	@Override
502
    public <T extends DefinedTermBase> List<T> getIncludes(Collection<T> partOf,	Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
503
		if (partOf == null || partOf.isEmpty()){
504
			return new ArrayList<>();
505
		}
506
		AuditEvent auditEvent = getAuditEventFromContext();
507
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
508
    		Query query = getSession().createQuery("select term from DefinedTermBase term where term.partOf in (:partOf)");
509
    		query.setParameterList("partOf", partOf);
510

    
511
    		addPageSizeAndNumber(query, pageSize, pageNumber);
512

    
513
		    @SuppressWarnings("unchecked")
514
            List<T> results = deduplicateResult(query.list());
515
		    defaultBeanInitializer.initializeAll(results, propertyPaths);
516
		    return results;
517
		} else {
518
			List<T> result = new ArrayList<>();
519
			for(T t : partOf) {
520
				AuditQuery query = makeAuditQuery(DefinedTermBase.class, auditEvent);
521
				query.add(AuditEntity.relatedId("partOf").eq(t.getId()));
522
				addPageSizeAndNumber(query, pageSize, pageNumber);
523

    
524
			    result.addAll(deduplicateResult(query.getResultList()));
525
			}
526
			defaultBeanInitializer.initializeAll(result, propertyPaths);
527
			return result;
528
		}
529
	}
530

    
531
	@Override
532
    public <T extends DefinedTermBase> long countPartOf(Set<T> definedTerms) {
533
		checkNotInPriorView("DefinedTermDaoImpl.countPartOf(Set<T> definedTerms)");
534
		Query query = getSession().createQuery("select count(distinct definedTerm) from DefinedTermBase definedTerm join definedTerm.includes included where included in (:definedTerms)");
535
		query.setParameterList("definedTerms", definedTerms);
536
		return (Long)query.uniqueResult();
537
	}
538

    
539
	@Override
540
    public <T extends DefinedTermBase> List<T> getPartOf(Set<T> definedTerms, Integer pageSize, Integer pageNumber, List<String> propertyPaths) {
541
		checkNotInPriorView("DefinedTermDaoImpl.getPartOf(Set<T> definedTerms, Integer pageSize, Integer pageNumber)");
542
		Query query = getSession().createQuery("select distinct definedTerm from DefinedTermBase definedTerm join definedTerm.includes included where included in (:definedTerms)");
543
		query.setParameterList("definedTerms", definedTerms);
544

    
545
		addPageSizeAndNumber(query, pageSize, pageNumber);
546

    
547
		@SuppressWarnings("unchecked")
548
        List<T> r = query.list();
549
		/**
550
		 * For some weird reason, hibernate returns proxies (extending the superclass), not the actual class on this,
551
		 * despite querying the damn database and returning the discriminator along with the rest of the object properties!
552
		 *
553
		 * Probably a bug in hibernate, but we'll manually deproxy for now since the objects are initialized anyway, the
554
		 * performance implications are small (we're swapping one array of references for another, not hitting the db or
555
		 * cache).
556
		 */
557
		List<T> results = new ArrayList<>();
558
		if(!definedTerms.isEmpty()) {
559
		    for(T t : r) {
560
		        T deproxied = CdmBase.deproxy(t);
561
                results.add(deproxied);
562
		    }
563
		    defaultBeanInitializer.initializeAll(results, propertyPaths);
564
		}
565
		return results;
566
	}
567

    
568
	@Override
569
    public DefinedTermBase findByUri(URI uri) {
570
		AuditEvent auditEvent = getAuditEventFromContext();
571
		if(auditEvent.equals(AuditEvent.CURRENT_VIEW)) {
572
		    Query query = getSession().createQuery("select term from DefinedTermBase term where term.uri = :uri");
573
		    query.setParameter("uri", uri);
574
		    return (DefinedTermBase<?>)query.uniqueResult();
575
		} else {
576
			AuditQuery query = makeAuditQuery(DefinedTermBase.class, auditEvent);
577
			query.add(AuditEntity.property("uri").eq(uri));
578
		    return (DefinedTermBase<?>)query.getSingleResult();
579
		}
580
	}
581

    
582
	@Override
583
	public <T extends DefinedTermBase> List<T> listByTermType(TermType termType, Integer limit, Integer start,
584
	        List<OrderHint> orderHints, List<String> propertyPaths) {
585
	    Query query = getSession().createQuery("SELECT term FROM DefinedTermBase term WHERE term.termType = :termType");
586
	    query.setParameter("termType", termType);
587

    
588
	    @SuppressWarnings("unchecked")
589
        List<T> result = deduplicateResult(query.list());
590

    
591
	    defaultBeanInitializer.initializeAll(result, propertyPaths);
592
        return result;
593
	}
594

    
595
	@Override
596
    public <TERM extends DefinedTermBase> List<TERM> listByTermClass(Class<TERM> clazz, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
597
//		checkNotInPriorView("DefinedTermDaoImpl.listByTermClass(Class<TERM> clazz, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths)");
598

    
599
		Query query = getSession().createQuery("FROM " + clazz.getSimpleName());
600

    
601
	    @SuppressWarnings("unchecked")
602
        List<TERM> result = deduplicateResult(query.list());
603

    
604
	    defaultBeanInitializer.initializeAll(result, propertyPaths);
605

    
606
	    return result;
607
	}
608

    
609
    @Override
610
    public <S extends DefinedTermBase> List<S> list(Class<S> type, Integer limit, Integer start,
611
            List<OrderHint> orderHints, List<String> propertyPath) {
612

    
613
        return deduplicateResult(super.list(type, limit, start, orderHints, propertyPath));
614
    }
615

    
616
    /**
617
     * Workaround for https://dev.e-taxonomy.eu/redmine/issues/5871 and #5945
618
     * Terms with multiple representations return identical duplicates
619
     * due to eager representation loading. We expect these duplicates to appear
620
     * in line wo we only compare one term with its predecessor. If it already
621
     * exists we remove it from the result.
622
     * @param orginals
623
     * @return
624
     */
625
    protected static <S extends CdmBase> List<S> deduplicateResult(List<S> orginals) {
626
        List<S> result = new ArrayList<>();
627
        Iterator<S> it = orginals.iterator();
628
        S last = null;
629
        while (it.hasNext()){
630
            S a = it.next();
631
            if (a != last){
632
                if (!result.contains(a)){
633
                    result.add(a);
634
                }
635
            }
636
            last = a;
637
        }
638
        return result;
639
    }
640

    
641
    @Override
642
    public <S extends DefinedTermBase> List<S> list(Class<S> clazz, List<TermVocabulary> vocs, Integer limit, String pattern) {
643
        return list(clazz, vocs, 0, limit, pattern, MatchMode.BEGINNING);
644
    }
645

    
646
    @Override
647
    public <S extends DefinedTermBase> List<S> list(Class<S> clazz, List<TermVocabulary> vocs, Integer pageNumber, Integer limit, String pattern, MatchMode matchmode){
648
        Session session = getSession();
649
        if (clazz == null){
650
            clazz = (Class)type;
651
        }
652
        Criteria crit = getSession().createCriteria(clazz, "term");
653
        if (!StringUtils.isBlank(pattern)){
654
            crit.createAlias("term.representations", "reps");
655
            Disjunction or = Restrictions.disjunction();
656
            if (matchmode == MatchMode.EXACT) {
657
                or.add(Restrictions.eq("titleCache", matchmode.queryStringFrom(pattern)));
658
                or.add(Restrictions.eq("reps.label", matchmode.queryStringFrom(pattern)));
659
            } else {
660
                or.add(Restrictions.like("titleCache", matchmode.queryStringFrom(pattern)));
661
                or.add(Restrictions.like("reps.label", matchmode.queryStringFrom(pattern)));
662
            }
663
            crit.add(or);
664
        }
665

    
666
        if (limit != null && limit >= 0) {
667
            crit.setMaxResults(limit);
668
        }
669

    
670
        if (vocs != null &&!vocs.isEmpty()){
671
            crit.createAlias("term.vocabulary", "voc");
672
            Disjunction or = Restrictions.disjunction();
673
            for (TermVocabulary<?> voc: vocs){
674
                Criterion criterion = Restrictions.eq("voc.id", voc.getId());
675
                or.add(criterion);
676
            }
677
            crit.add(or);
678
        }
679

    
680
        crit.addOrder(Order.asc("titleCache"));
681
        if (limit == null){
682
            limit = 1;
683
        }
684
        crit.setFirstResult(0);
685
        @SuppressWarnings("unchecked")
686
        List<S> results = deduplicateResult(crit.list());
687
        return results;
688
    }
689

    
690
    @Override
691
    public <S extends DefinedTermBase> List<S> listByAbbrev(Class<S> clazz, List<TermVocabulary> vocs, Integer limit, String pattern, TermSearchField type) {
692
        return listByAbbrev(clazz, vocs, 0, limit, pattern, MatchMode.BEGINNING, type);
693
    }
694

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

    
698
        if (clazz == null){
699
            clazz = (Class)type;
700
        }
701
        Criteria crit = getSession().createCriteria(clazz, "type");
702
        if (!StringUtils.isBlank(pattern)){
703
            if (matchmode == MatchMode.EXACT) {
704
                crit.add(Restrictions.eq(abbrevType.getKey(), matchmode.queryStringFrom(pattern)));
705
            } else {
706
                crit.add(Restrictions.like(abbrevType.getKey(), matchmode.queryStringFrom(pattern)));
707
            }
708
        }
709
        if (limit != null && limit >= 0) {
710
            crit.setMaxResults(limit);
711
        }
712

    
713
        if (vocs != null &&!vocs.isEmpty()){
714
            crit.createAlias("type.vocabulary", "voc");
715
            Disjunction or = Restrictions.disjunction();
716
            for (TermVocabulary<?> voc: vocs){
717
                Criterion criterion = Restrictions.eq("voc.id", voc.getId());
718
                or.add(criterion);
719
            }
720
            crit.add(or);
721
        }
722

    
723
        crit.addOrder(Order.asc(abbrevType.getKey()));
724
        if (limit == null){
725
            limit = 1;
726
        }
727
//        int firstItem = (pageNumber - 1) * limit;
728

    
729
        crit.setFirstResult(0);
730
        @SuppressWarnings("unchecked")
731
        List<S> results = deduplicateResult(crit.list());
732
        return results;
733
    }
734

    
735

    
736
    @Override
737
    public Collection<TermDto> getIncludesAsDto(
738
            TermDto parentTerm) {
739
        String queryString;
740
        if (parentTerm.getTermType().equals(TermType.NamedArea)){
741
            queryString = TermDto.getTermDtoSelectNamedArea();
742
        }else{
743
            queryString = TermDto.getTermDtoSelect();
744
        }
745
        queryString = queryString
746
                + "where a.partOf.uuid = :parentUuid";
747
        Query query =  getSession().createQuery(queryString);
748
        query.setParameter("parentUuid", parentTerm.getUuid());
749

    
750
        @SuppressWarnings("unchecked")
751
        List<Object[]> result = query.list();
752

    
753
        List<TermDto> list = TermDto.termDtoListFrom(result);
754
        return list;
755
    }
756

    
757
    @Override
758
    public Collection<TermDto> getKindOfsAsDto(TermDto parentTerm) {
759

    
760
        String queryString;
761
        if (parentTerm.getTermType().equals(TermType.NamedArea)){
762
            queryString = TermDto.getTermDtoSelectNamedArea();
763
        }else{
764
            queryString = TermDto.getTermDtoSelect();
765
        }
766
        queryString = queryString + "where a.kindOf.uuid = :parentUuid";
767
        Query query =  getSession().createQuery(queryString);
768
        query.setParameter("parentUuid", parentTerm.getUuid());
769

    
770
        @SuppressWarnings("unchecked")
771
        List<Object[]> result = query.list();
772

    
773
        List<TermDto> list = TermDto.termDtoListFrom(result);
774
        return list;
775
    }
776

    
777
    @Override
778
    public Collection<TermDto> findByTitleAsDto(String title, TermType termType) {
779
        String queryString = TermDto.getTermDtoSelect()
780
                + " where a.titleCache like :title "
781
                + (termType!=null?" and a.termType = :termType ":"");
782

    
783
        title = title.replace("*", "%");
784
        Query query =  getSession().createQuery(queryString);
785
        query.setParameter("title", "%"+title+"%");
786
        if(termType!=null){
787
            query.setParameter("termType", termType);
788
        }
789

    
790
        @SuppressWarnings("unchecked")
791
        List<Object[]> result = query.list();
792

    
793
        List<TermDto> list = TermDto.termDtoListFrom(result);
794
        return list;
795
    }
796

    
797
    @Override
798
    public TermDto findByUUIDAsDto(UUID uuid) {
799
        String queryString = TermDto.getTermDtoSelect()
800
                + " where a.uuid like :uuid ";
801

    
802

    
803
        Query query =  getSession().createQuery(queryString);
804
        query.setParameter("uuid", uuid);
805

    
806

    
807
        @SuppressWarnings("unchecked")
808
        List<Object[]> result = query.list();
809

    
810
        List<TermDto> list = TermDto.termDtoListFrom(result);
811
        if (list.size()== 1){
812
            return list.get(0);
813
        }else{
814
            return null;
815
        }
816

    
817
    }
818

    
819

    
820
    @Override
821
    public Collection<TermDto> findByTypeAsDto(TermType termType) {
822
        if (termType == null){
823
            return null;
824
        }
825
        String queryString = TermDto.getTermDtoSelect()
826
                + " where a.termType = :termType ";
827
        Query query =  getSession().createQuery(queryString);
828

    
829
        query.setParameter("termType", termType);
830

    
831
        @SuppressWarnings("unchecked")
832
        List<Object[]> result = query.list();
833

    
834
        List<TermDto> list = TermDto.termDtoListFrom(result);
835
        return list;
836
    }
837

    
838
    @Override
839
    public Collection<TermDto> findByUriAsDto(URI uri, String termLabel, TermType termType) {
840
        String queryString = TermDto.getTermDtoSelect()
841
                + " where a.uri like :uri "
842
                + (termType!=null?" and a.termType = :termType ":"")
843
                + (termLabel!=null?" and a.titleCache = :termLabel ":"")
844
                ;
845
        Query query =  getSession().createQuery(queryString);
846
        query.setParameter("uri", uri.toString());
847
        if(termLabel!=null){
848
            query.setParameter("termLabel", "%"+termLabel+"%");
849
        }
850
        if(termType!=null){
851
            query.setParameter("termType", termType);
852
        }
853

    
854
        @SuppressWarnings("unchecked")
855
        List<Object[]> result = query.list();
856

    
857
        List<TermDto> list = TermDto.termDtoListFrom(result);
858
        return list;
859
    }
860

    
861
    @Override
862
    public  Map<UUID, List<TermDto>> getSupportedStatesForFeature(Set<UUID> featureUuids){
863
        Map<UUID, List<TermDto>> map = new HashMap<>();
864
        for (UUID featureUuid: featureUuids){
865
            List<TermDto> list = new ArrayList<>();
866
            String supportedCategoriesQueryString = "SELECT cat.uuid "
867
                    + "from DefinedTermBase t "
868
                    + "join t.supportedCategoricalEnumerations as cat "
869
                    + "where t.uuid = :featureUuid";
870
            Query supportedCategoriesQuery =  getSession().createQuery(supportedCategoriesQueryString);
871
            supportedCategoriesQuery.setParameter("featureUuid", featureUuid);
872
            @SuppressWarnings("unchecked")
873
    		List<UUID> supportedCategories = supportedCategoriesQuery.list();
874
            if(supportedCategories.isEmpty()){
875
                map.put(featureUuid, list);
876
                continue;
877
            }
878

    
879
            String queryString = TermDto.getTermDtoSelect()
880
                    + "where v.uuid in (:supportedCategories) "
881
                    + "order by a.titleCache";
882
            Query query =  getSession().createQuery(queryString);
883
            query.setParameterList("supportedCategories", supportedCategories);
884

    
885
            @SuppressWarnings("unchecked")
886
            List<Object[]> result = query.list();
887

    
888
            list = TermDto.termDtoListFrom(result);
889
            map.put(featureUuid, list);
890
        }
891
        return map;
892
    }
893

    
894
    @Override
895
    public Collection<TermDto> findByUUIDsAsDto(List<UUID> uuidList) {
896
        List<TermDto> list = new ArrayList<>();
897
        if (uuidList == null || uuidList.isEmpty()){
898
            return null;
899
        }
900

    
901
        String queryString = TermDto.getTermDtoSelect()
902
                + "where a.uuid in :uuidList "
903
                + "order by a.titleCache";
904
        Query query =  getSession().createQuery(queryString);
905
        query.setParameterList("uuidList", uuidList);
906

    
907
        @SuppressWarnings("unchecked")
908
        List<Object[]> result = query.list();
909

    
910
        list = TermDto.termDtoListFrom(result);
911
        return list;
912
    }
913

    
914
    @Override
915
    public Collection<TermDto> findFeatureByUUIDsAsDto(List<UUID> uuidList) {
916
        List<TermDto> list = new ArrayList<>();
917
        if (uuidList == null || uuidList.isEmpty()){
918
            return null;
919
        }
920

    
921
        String queryString = FeatureDto.getTermDtoSelect()
922
                + "where a.uuid in :uuidList "
923
                + "order by a.titleCache";
924
        Query query =  getSession().createQuery(queryString);
925
        query.setParameterList("uuidList", uuidList);
926

    
927
        @SuppressWarnings("unchecked")
928
        List<Object[]> result = query.list();
929

    
930
        list = FeatureDto.termDtoListFrom(result);
931
        return list;
932
    }
933

    
934
    @Override
935
    public Collection<TermDto> findFeatureByTitleAsDto(String pattern) {
936
        String queryString = FeatureDto.getTermDtoSelect()
937
                + " where a.titleCache like :title "
938
                +  " and a.termType = :termType ";
939

    
940
        pattern = pattern.replace("*", "%");
941
        Query query =  getSession().createQuery(queryString);
942
        query.setParameter("title", "%"+pattern+"%");
943
        query.setParameter("termType", TermType.Feature);
944

    
945

    
946
        @SuppressWarnings("unchecked")
947
        List<Object[]> result = query.list();
948

    
949
        List<TermDto> list = FeatureDto.termDtoListFrom(result);
950
        return list;
951
    }
952

    
953
    @Override
954
    public TermDto getTermDto(UUID uuid) {
955
        String queryString = TermDto.getTermDtoSelect()
956
                + " where a.uuid = :uuid ";
957

    
958

    
959
        Query query =  getSession().createQuery(queryString);
960
        query.setParameter("uuid", uuid);
961

    
962

    
963

    
964
        @SuppressWarnings("unchecked")
965
        List<Object[]> result = query.list();
966
        TermDto dto = null;
967
        List<TermDto> dtoList = TermDto.termDtoListFrom(result);
968
        if (dtoList != null && !dtoList.isEmpty()){
969
            dto = dtoList.get(0);
970
        }
971
        return dto;
972
    }
973
}
(1-1/6)