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.taxon;
|
11
|
|
12
|
import java.math.BigInteger;
|
13
|
import java.util.ArrayList;
|
14
|
import java.util.Collection;
|
15
|
import java.util.Collections;
|
16
|
import java.util.Comparator;
|
17
|
import java.util.HashMap;
|
18
|
import java.util.HashSet;
|
19
|
import java.util.Iterator;
|
20
|
import java.util.List;
|
21
|
import java.util.Map;
|
22
|
import java.util.Set;
|
23
|
import java.util.UUID;
|
24
|
|
25
|
import org.apache.log4j.Logger;
|
26
|
import org.hibernate.Criteria;
|
27
|
import org.hibernate.Hibernate;
|
28
|
import org.hibernate.Query;
|
29
|
import org.hibernate.criterion.Projections;
|
30
|
import org.hibernate.criterion.Restrictions;
|
31
|
import org.springframework.beans.factory.annotation.Autowired;
|
32
|
import org.springframework.beans.factory.annotation.Qualifier;
|
33
|
import org.springframework.stereotype.Repository;
|
34
|
|
35
|
import eu.etaxonomy.cdm.common.CdmUtils;
|
36
|
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
|
37
|
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
|
38
|
import eu.etaxonomy.cdm.model.common.CdmBase;
|
39
|
import eu.etaxonomy.cdm.model.common.TreeIndex;
|
40
|
import eu.etaxonomy.cdm.model.name.Rank;
|
41
|
import eu.etaxonomy.cdm.model.name.TaxonName;
|
42
|
import eu.etaxonomy.cdm.model.reference.NamedSource;
|
43
|
import eu.etaxonomy.cdm.model.reference.Reference;
|
44
|
import eu.etaxonomy.cdm.model.taxon.Classification;
|
45
|
import eu.etaxonomy.cdm.model.taxon.SecundumSource;
|
46
|
import eu.etaxonomy.cdm.model.taxon.Synonym;
|
47
|
import eu.etaxonomy.cdm.model.taxon.Taxon;
|
48
|
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
|
49
|
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
|
50
|
import eu.etaxonomy.cdm.model.taxon.TaxonNodeAgentRelation;
|
51
|
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
|
52
|
import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
|
53
|
import eu.etaxonomy.cdm.persistence.dao.hibernate.common.AnnotatableDaoImpl;
|
54
|
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
|
55
|
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
|
56
|
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
|
57
|
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonRelationshipDao;
|
58
|
import eu.etaxonomy.cdm.persistence.dto.SortableTaxonNodeQueryResult;
|
59
|
import eu.etaxonomy.cdm.persistence.dto.SortableTaxonNodeQueryResultComparator;
|
60
|
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
|
61
|
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
|
62
|
import eu.etaxonomy.cdm.persistence.query.OrderHint;
|
63
|
|
64
|
/**
|
65
|
* @author a.mueller
|
66
|
* @since 16.06.2009
|
67
|
*/
|
68
|
@Repository
|
69
|
@Qualifier("taxonNodeDaoHibernateImpl")
|
70
|
public class TaxonNodeDaoHibernateImpl extends AnnotatableDaoImpl<TaxonNode>
|
71
|
implements ITaxonNodeDao {
|
72
|
|
73
|
private static final Logger logger = Logger.getLogger(TaxonNodeDaoHibernateImpl.class);
|
74
|
|
75
|
private static final int DEFAULT_SET_SUBTREE_PARTITION_SIZE = 100;
|
76
|
|
77
|
@Autowired
|
78
|
private ITaxonDao taxonDao;
|
79
|
@Autowired
|
80
|
private IClassificationDao classificationDao;
|
81
|
@Autowired
|
82
|
private ITaxonRelationshipDao taxonRelDao;
|
83
|
|
84
|
public TaxonNodeDaoHibernateImpl() {
|
85
|
super(TaxonNode.class);
|
86
|
}
|
87
|
|
88
|
@Override
|
89
|
public UUID delete(TaxonNode persistentObject, boolean deleteChildren){
|
90
|
Taxon taxon = persistentObject.getTaxon();
|
91
|
taxon = HibernateProxyHelper.deproxy(taxon);
|
92
|
|
93
|
/*Session session = this.getSession();
|
94
|
Query query = session.createQuery("from TaxonNode t where t.taxon = :taxon");
|
95
|
query.setParameter("taxon", taxon);
|
96
|
List result = query.list();*/
|
97
|
if (taxon != null){
|
98
|
Hibernate.initialize(taxon);
|
99
|
Hibernate.initialize(taxon.getTaxonNodes());
|
100
|
Set<TaxonNode> nodes = taxon.getTaxonNodes();
|
101
|
//Hibernate.initialize(taxon.getTaxonNodes());
|
102
|
for (TaxonNode node:nodes) {
|
103
|
node = HibernateProxyHelper.deproxy(node);
|
104
|
|
105
|
if (node.equals(persistentObject)){
|
106
|
if (node.hasChildNodes()){
|
107
|
Iterator<TaxonNode> childNodes = node.getChildNodes().iterator();
|
108
|
TaxonNode childNode;
|
109
|
List<TaxonNode> listForDeletion = new ArrayList<>();
|
110
|
while (childNodes.hasNext()){
|
111
|
childNode = childNodes.next();
|
112
|
listForDeletion.add(childNode);
|
113
|
childNodes.remove();
|
114
|
|
115
|
}
|
116
|
for (TaxonNode deleteNode:listForDeletion){
|
117
|
delete(deleteNode, deleteChildren);
|
118
|
}
|
119
|
}
|
120
|
|
121
|
taxon.removeTaxonNode(node, deleteChildren);
|
122
|
taxonDao.saveOrUpdate(taxon);
|
123
|
taxon = HibernateProxyHelper.deproxy(taxonDao.findByUuid(taxon.getUuid()), Taxon.class);
|
124
|
taxonDao.delete(taxon);
|
125
|
|
126
|
}
|
127
|
}
|
128
|
}
|
129
|
|
130
|
UUID result = super.delete(persistentObject);
|
131
|
return result;
|
132
|
}
|
133
|
|
134
|
@Override
|
135
|
public List<TaxonNode> getTaxonOfAcceptedTaxaByClassification(Classification classification, Integer start, Integer end) {
|
136
|
int classificationId = classification.getId();
|
137
|
String limit = "";
|
138
|
if(start !=null && end != null){
|
139
|
limit = "LIMIT "+start+"," +end;
|
140
|
}
|
141
|
//FIXME write test
|
142
|
String queryString = "SELECT DISTINCT nodes.*,taxa.titleCache "
|
143
|
+ " FROM TaxonNode AS nodes "
|
144
|
+ " LEFT JOIN TaxonBase AS taxa ON nodes.taxon_id = taxa.id "
|
145
|
+ " WHERE taxa.DTYPE = 'Taxon' "
|
146
|
+ " AND nodes.classification_id = " + classificationId +
|
147
|
" ORDER BY taxa.titleCache " + limit;
|
148
|
@SuppressWarnings("unchecked")
|
149
|
List<TaxonNode> result = getSession().createSQLQuery(queryString).addEntity(TaxonNode.class).list();
|
150
|
|
151
|
return result;
|
152
|
}
|
153
|
|
154
|
@Override
|
155
|
public int countTaxonOfAcceptedTaxaByClassification(Classification classification){
|
156
|
int classificationId = classification.getId();
|
157
|
//FIXME write test
|
158
|
String queryString = ""
|
159
|
+ " SELECT DISTINCT COUNT('nodes.*') "
|
160
|
+ " FROM TaxonNode AS nodes "
|
161
|
+ " LEFT JOIN TaxonBase AS taxa ON nodes.taxon_id = taxa.id "
|
162
|
+ " WHERE taxa.DTYPE = 'Taxon' AND nodes.classification_id = " + classificationId;
|
163
|
@SuppressWarnings("unchecked")
|
164
|
List<BigInteger> result = getSession().createSQLQuery(queryString).list();
|
165
|
return result.get(0).intValue ();
|
166
|
}
|
167
|
|
168
|
@Override
|
169
|
public List<TaxonNodeDto> listChildNodesAsUuidAndTitleCache(TaxonNodeDto parent) {
|
170
|
String queryString =
|
171
|
" SELECT tn.uuid, tn.id, t.titleCache "
|
172
|
+ " FROM TaxonNode tn "
|
173
|
+ " INNER JOIN tn.taxon AS t "
|
174
|
+ " WHERE tn.parent.uuid = :parentId";
|
175
|
|
176
|
Query query = getSession().createQuery(queryString);
|
177
|
query.setParameter("parentId", parent.getUuid());
|
178
|
|
179
|
@SuppressWarnings("unchecked")
|
180
|
List<Object[]> result = query.list();
|
181
|
|
182
|
List<TaxonNodeDto> list = new ArrayList<>();
|
183
|
for(Object[] object : result){
|
184
|
list.add(new TaxonNodeDto((UUID) object[0],(Integer) object[1], (String) object[2]));
|
185
|
}
|
186
|
return list;
|
187
|
}
|
188
|
|
189
|
@Override
|
190
|
public List<TaxonNodeDto> listChildNodesAsTaxonNodeDto(TaxonNodeDto parent) {
|
191
|
String queryString =
|
192
|
" SELECT tn "
|
193
|
+ " FROM TaxonNode tn "
|
194
|
+ " INNER JOIN tn.taxon AS t "
|
195
|
+ " WHERE tn.parent.uuid = :parentId";
|
196
|
Query query = getSession().createQuery(queryString);
|
197
|
query.setParameter("parentId", parent.getUuid());
|
198
|
|
199
|
@SuppressWarnings("unchecked")
|
200
|
List<TaxonNode> result = query.list();
|
201
|
|
202
|
List<TaxonNodeDto> list = new ArrayList<>();
|
203
|
for(TaxonNode object : result){
|
204
|
list.add(new TaxonNodeDto(object));
|
205
|
}
|
206
|
return list;
|
207
|
}
|
208
|
|
209
|
@Override
|
210
|
public List<TaxonNodeDto> getUuidAndTitleCache(Integer limit, String pattern, UUID classificationUuid, boolean includeDoubtful) {
|
211
|
|
212
|
Query query = createQueryForUuidAndTitleCache(limit, classificationUuid, pattern, includeDoubtful);
|
213
|
@SuppressWarnings("unchecked")
|
214
|
List<SortableTaxonNodeQueryResult> result = query.list();
|
215
|
Collections.sort(result, new SortableTaxonNodeQueryResultComparator());
|
216
|
if(logger.isTraceEnabled()){
|
217
|
logger.trace("number of matches:" + result.size());
|
218
|
result.stream().forEach(o -> logger.trace("uuid: " + o.getTaxonNodeUuid() + " titleCache:" + o.getTaxonTitleCache() + " rank: " + o.getNameRank()));
|
219
|
}
|
220
|
List<TaxonNodeDto> list = new ArrayList<>();
|
221
|
// int index = limit;
|
222
|
for(SortableTaxonNodeQueryResult stnqr : result){
|
223
|
// if (index > 0){
|
224
|
list.add(new TaxonNodeDto(stnqr.getTaxonNodeUuid(),stnqr.getTaxonNodeId(), stnqr.getTaxonTitleCache()));
|
225
|
// index --;
|
226
|
// }
|
227
|
|
228
|
}
|
229
|
|
230
|
return list;
|
231
|
}
|
232
|
|
233
|
private Query createQueryForUuidAndTitleCache(Integer limit, UUID classificationUuid, String pattern, boolean includeDoubtful){
|
234
|
String doubtfulPattern = "";
|
235
|
String queryString = "SELECT new " + SortableTaxonNodeQueryResult.class.getName() + "("
|
236
|
+ " node.uuid, node.id, t.titleCache, rank"
|
237
|
+ ") "
|
238
|
+ " FROM TaxonNode AS node "
|
239
|
+ " JOIN node.taxon as t " // FIXME why not inner join here?
|
240
|
+ " INNER JOIN t.name AS name "
|
241
|
+ " LEFT OUTER JOIN name.rank AS rank "
|
242
|
+ " WHERE ";
|
243
|
|
244
|
if (classificationUuid != null){
|
245
|
queryString = queryString + " node.classification.uuid like :classificationUuid " ;
|
246
|
}
|
247
|
if (pattern == null){
|
248
|
pattern = "*";
|
249
|
}
|
250
|
if (pattern != null){
|
251
|
if (pattern.equals("?")){
|
252
|
limit = null;
|
253
|
} else{
|
254
|
if (!pattern.endsWith("*")){
|
255
|
pattern += "%";
|
256
|
}
|
257
|
pattern = pattern.replace("*", "%");
|
258
|
pattern = pattern.replace("?", "%");
|
259
|
if (classificationUuid != null){
|
260
|
queryString = queryString + " AND ";
|
261
|
}
|
262
|
queryString = queryString + " (t.titleCache LIKE (:pattern) " ;
|
263
|
doubtfulPattern = "?" + pattern;
|
264
|
if (includeDoubtful){
|
265
|
queryString = queryString + " OR t.titleCache LIKE (:doubtfulPattern))";
|
266
|
}else{
|
267
|
queryString = queryString + ")";
|
268
|
}
|
269
|
}
|
270
|
|
271
|
}
|
272
|
|
273
|
Query query = getSession().createQuery(queryString);
|
274
|
if (pattern != null){
|
275
|
query.setParameter("pattern", pattern);
|
276
|
}
|
277
|
if (includeDoubtful){
|
278
|
query.setParameter("doubtfulPattern", doubtfulPattern);
|
279
|
}
|
280
|
|
281
|
if(classificationUuid != null){
|
282
|
query.setParameter("classificationUuid", classificationUuid);
|
283
|
}
|
284
|
if (limit != null){
|
285
|
query.setMaxResults(limit);
|
286
|
}
|
287
|
return query;
|
288
|
}
|
289
|
|
290
|
|
291
|
|
292
|
@Override
|
293
|
public TaxonNodeDto getParentUuidAndTitleCache(TaxonNodeDto child) {
|
294
|
String queryString = ""
|
295
|
+ " SELECT tn.parent.uuid, tn.parent.id, tn.parent.taxon.titleCache, "
|
296
|
+ " tn.parent.classification.titleCache "
|
297
|
+ " FROM TaxonNode tn"
|
298
|
+ " LEFT OUTER JOIN tn.parent.taxon"
|
299
|
+ " WHERE tn.id = :childId";
|
300
|
Query query = getSession().createQuery(queryString);
|
301
|
query.setParameter("childId", child.getId());
|
302
|
List<TaxonNodeDto> list = new ArrayList<>();
|
303
|
|
304
|
@SuppressWarnings("unchecked")
|
305
|
List<Object[]> result = query.list();
|
306
|
|
307
|
for(Object[] object : result){
|
308
|
UUID uuid = (UUID) object[0];
|
309
|
Integer id = (Integer) object[1];
|
310
|
String taxonTitleCache = (String) object[2];
|
311
|
String classificationTitleCache = (String) object[3];
|
312
|
if(taxonTitleCache!=null){
|
313
|
list.add(new TaxonNodeDto(uuid,id, taxonTitleCache));
|
314
|
}
|
315
|
else{
|
316
|
list.add(new TaxonNodeDto(uuid,id, classificationTitleCache));
|
317
|
}
|
318
|
}
|
319
|
if(list.size()==1){
|
320
|
return list.iterator().next();
|
321
|
}
|
322
|
return null;
|
323
|
}
|
324
|
@Override
|
325
|
public List<TaxonNode> listChildrenOf(TaxonNode node, Integer pageSize, Integer pageIndex,
|
326
|
boolean recursive, boolean includeUnpublished, List<String> propertyPaths, Comparator<TaxonNode> comparator){
|
327
|
return listChildrenOfRecursive(node,new ArrayList<>(), pageSize, pageIndex, recursive, includeUnpublished, propertyPaths, comparator);
|
328
|
}
|
329
|
|
330
|
private List<TaxonNode> listChildrenOfRecursive(TaxonNode node, List<TaxonNode> previousResult, Integer pageSize, Integer pageIndex,
|
331
|
boolean recursive, boolean includeUnpublished, List<String> propertyPaths, Comparator<TaxonNode> comparator){
|
332
|
|
333
|
if (recursive == true && comparator == null ){
|
334
|
Criteria crit = childrenOfCriteria(node, includeUnpublished);
|
335
|
|
336
|
this.addPageSizeAndNumber(crit, pageSize, pageIndex);
|
337
|
@SuppressWarnings("unchecked")
|
338
|
List<TaxonNode> results = crit.list();
|
339
|
results.remove(node);
|
340
|
defaultBeanInitializer.initializeAll(results, propertyPaths);
|
341
|
return results;
|
342
|
|
343
|
} else if (recursive == true){
|
344
|
List<TaxonNode> children = node.getChildNodes();
|
345
|
Collections.sort(children, comparator);
|
346
|
for (TaxonNode child: children){
|
347
|
if (!previousResult.contains(child)){
|
348
|
previousResult.add(child);
|
349
|
}
|
350
|
if (child.hasChildNodes()){
|
351
|
previousResult = listChildrenOfRecursive(child, previousResult, pageSize, pageIndex,
|
352
|
recursive, includeUnpublished, propertyPaths, comparator);
|
353
|
}
|
354
|
}
|
355
|
return previousResult;
|
356
|
|
357
|
} else{
|
358
|
return classificationDao.listChildrenOf(node.getTaxon(), node.getClassification(), null,
|
359
|
includeUnpublished, pageSize, pageIndex, propertyPaths);
|
360
|
}
|
361
|
}
|
362
|
|
363
|
@Override
|
364
|
public Long countChildrenOf(TaxonNode node, Classification classification,
|
365
|
boolean recursive, boolean includeUnpublished) {
|
366
|
|
367
|
if (recursive == true){
|
368
|
Criteria crit = childrenOfCriteria(node, includeUnpublished);
|
369
|
crit.setProjection(Projections.rowCount());
|
370
|
return ((Integer)crit.uniqueResult().hashCode()).longValue();
|
371
|
}else{
|
372
|
return classificationDao.countChildrenOf(
|
373
|
node.getTaxon(), classification, null, includeUnpublished);
|
374
|
}
|
375
|
}
|
376
|
|
377
|
private Criteria childrenOfCriteria(TaxonNode node, boolean includeUnpublished) {
|
378
|
Criteria crit = getSession().createCriteria(TaxonNode.class);
|
379
|
crit.add( Restrictions.like("treeIndex", node.treeIndex()+ "%") );
|
380
|
if (!includeUnpublished){
|
381
|
crit.createCriteria("taxon").add( Restrictions.eq("publish", Boolean.TRUE));
|
382
|
}
|
383
|
return crit;
|
384
|
}
|
385
|
|
386
|
@Override
|
387
|
public List<TaxonNodeAgentRelation> listTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid,
|
388
|
UUID agentUuid, UUID rankUuid, UUID relTypeUuid, Integer start, Integer limit,
|
389
|
List<String> propertyPaths) {
|
390
|
|
391
|
StringBuilder hql = prepareListTaxonNodeAgentRelations(taxonUuid, classificationUuid,
|
392
|
agentUuid, rankUuid, relTypeUuid, false);
|
393
|
|
394
|
Query query = getSession().createQuery(hql.toString());
|
395
|
|
396
|
if(limit != null) {
|
397
|
query.setMaxResults(limit);
|
398
|
if(start != null) {
|
399
|
query.setFirstResult(start);
|
400
|
}
|
401
|
}
|
402
|
|
403
|
setParamsForListTaxonNodeAgentRelations(taxonUuid, classificationUuid, agentUuid, rankUuid, relTypeUuid, query);
|
404
|
|
405
|
@SuppressWarnings("unchecked")
|
406
|
List<TaxonNodeAgentRelation> records = query.list();
|
407
|
|
408
|
if(propertyPaths != null) {
|
409
|
defaultBeanInitializer.initializeAll(records, propertyPaths);
|
410
|
}
|
411
|
return records;
|
412
|
}
|
413
|
|
414
|
@Override
|
415
|
public <S extends TaxonNode> List<S> list(Class<S> type, List<Restriction<?>> restrictions, Integer limit,
|
416
|
Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {
|
417
|
// TODO Auto-generated method stub
|
418
|
return list(type, restrictions, limit, start, orderHints, propertyPaths, INCLUDE_UNPUBLISHED);
|
419
|
}
|
420
|
|
421
|
@Override
|
422
|
public <S extends TaxonNode> List<S> list(Class<S> type, List<Restriction<?>> restrictions, Integer limit,
|
423
|
Integer start, List<OrderHint> orderHints, List<String> propertyPaths, boolean includePublished) {
|
424
|
|
425
|
Criteria criteria = createCriteria(type, restrictions, false);
|
426
|
|
427
|
if(!includePublished){
|
428
|
criteria.add(Restrictions.eq("taxon.publish", true));
|
429
|
}
|
430
|
|
431
|
addLimitAndStart(criteria, limit, start);
|
432
|
addOrder(criteria, orderHints);
|
433
|
|
434
|
@SuppressWarnings("unchecked")
|
435
|
List<S> result = criteria.list();
|
436
|
defaultBeanInitializer.initializeAll(result, propertyPaths);
|
437
|
return result;
|
438
|
}
|
439
|
|
440
|
@Override
|
441
|
public long count(Class<? extends TaxonNode> type, List<Restriction<?>> restrictions) {
|
442
|
return count(type, restrictions, INCLUDE_UNPUBLISHED);
|
443
|
}
|
444
|
|
445
|
|
446
|
@Override
|
447
|
public long count(Class<? extends TaxonNode> type, List<Restriction<?>> restrictions, boolean includePublished) {
|
448
|
|
449
|
Criteria criteria = createCriteria(type, restrictions, false);
|
450
|
if(!includePublished){
|
451
|
criteria.add(Restrictions.eq("taxon.publish", true));
|
452
|
}
|
453
|
criteria.setProjection(Projections.projectionList().add(Projections.rowCount()));
|
454
|
return (Long) criteria.uniqueResult();
|
455
|
}
|
456
|
|
457
|
@Override
|
458
|
public long countTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid, UUID agentUuid, UUID rankUuid, UUID relTypeUuid) {
|
459
|
|
460
|
StringBuilder hql = prepareListTaxonNodeAgentRelations(taxonUuid, classificationUuid, agentUuid, rankUuid, relTypeUuid, true);
|
461
|
Query query = getSession().createQuery(hql.toString());
|
462
|
|
463
|
setParamsForListTaxonNodeAgentRelations(taxonUuid, classificationUuid, agentUuid, rankUuid, relTypeUuid, query);
|
464
|
|
465
|
Long count = Long.parseLong(query.uniqueResult().toString());
|
466
|
|
467
|
return count;
|
468
|
}
|
469
|
|
470
|
/**
|
471
|
* @param taxonUuid
|
472
|
* @param classificationUuid
|
473
|
* @param agentUuid
|
474
|
* @param relTypeUuid TODO
|
475
|
* @param doCount TODO
|
476
|
* @param rankId
|
477
|
* limit to taxa having this rank, only applies if <code>taxonUuid = null</code>
|
478
|
* @return
|
479
|
*/
|
480
|
private StringBuilder prepareListTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid, UUID agentUuid, UUID rankUuid, UUID relTypeUuid, boolean doCount) {
|
481
|
|
482
|
StringBuilder hql = new StringBuilder();
|
483
|
|
484
|
String join_fetch_mode = doCount ? "JOIN" : "JOIN FETCH";
|
485
|
|
486
|
if(doCount) {
|
487
|
hql.append("SELECT COUNT(tnar)");
|
488
|
} else {
|
489
|
hql.append("SELECT tnar");
|
490
|
}
|
491
|
|
492
|
hql.append(" FROM TaxonNodeAgentRelation AS tnar ");
|
493
|
if(taxonUuid != null) {
|
494
|
// taxonUuid is search filter, do not fetch it
|
495
|
hql.append(" JOIN tnar.taxonNode AS tn "
|
496
|
+ " JOIN tn.taxon AS t ");
|
497
|
} else {
|
498
|
hql.append(join_fetch_mode)
|
499
|
.append(" tnar.taxonNode AS tn ")
|
500
|
.append(join_fetch_mode).append(" tn.taxon AS t ");
|
501
|
if(rankUuid != null) {
|
502
|
hql.append(" join t.name as n ");
|
503
|
}
|
504
|
}
|
505
|
hql.append(" JOIN tn.classification AS c ");
|
506
|
if(agentUuid != null) {
|
507
|
// agentUuid is search filter, do not fetch it
|
508
|
// hql.append(" join tnar.agent as a ");
|
509
|
hql.append(join_fetch_mode).append(" tnar.agent AS a ");
|
510
|
} else {
|
511
|
hql.append(join_fetch_mode).append(" tnar.agent AS a ");
|
512
|
}
|
513
|
|
514
|
hql.append(" WHERE (1 = 1) ");
|
515
|
|
516
|
if(relTypeUuid != null) {
|
517
|
hql.append(" AND tnar.type.uuid = :relTypeUuid ");
|
518
|
}
|
519
|
|
520
|
if(taxonUuid != null) {
|
521
|
hql.append(" AND t.uuid = :taxonUuid ");
|
522
|
} else {
|
523
|
if(rankUuid != null) {
|
524
|
hql.append(" AND n.rank.uuid = :rankUuid ");
|
525
|
}
|
526
|
}
|
527
|
if(classificationUuid != null) {
|
528
|
hql.append(" AND c.uuid = :classificationUuid ");
|
529
|
}
|
530
|
if(agentUuid != null) {
|
531
|
hql.append(" AND a.uuid = :agentUuid ");
|
532
|
}
|
533
|
|
534
|
hql.append(" ORDER BY a.titleCache");
|
535
|
return hql;
|
536
|
}
|
537
|
|
538
|
/**
|
539
|
* @param taxonUuid
|
540
|
* @param classificationUuid
|
541
|
* @param agentUuid
|
542
|
* @param relTypeUuid TODO
|
543
|
* @param query
|
544
|
* @param rankId TODO
|
545
|
*/
|
546
|
private void setParamsForListTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid, UUID agentUuid,
|
547
|
UUID rankUuid, UUID relTypeUuid, Query query) {
|
548
|
|
549
|
if(taxonUuid != null) {
|
550
|
query.setParameter("taxonUuid", taxonUuid);
|
551
|
} else {
|
552
|
if(rankUuid != null) {
|
553
|
query.setParameter("rankUuid", rankUuid);
|
554
|
}
|
555
|
}
|
556
|
if(classificationUuid != null) {
|
557
|
query.setParameter("classificationUuid", classificationUuid);
|
558
|
}
|
559
|
if(agentUuid != null) {
|
560
|
query.setParameter("agentUuid", agentUuid);
|
561
|
}
|
562
|
if(relTypeUuid != null) {
|
563
|
query.setParameter("relTypeUuid", relTypeUuid);
|
564
|
}
|
565
|
}
|
566
|
|
567
|
@Override
|
568
|
public Map<TreeIndex, Integer> rankOrderIndexForTreeIndex(List<TreeIndex> treeIndexes,
|
569
|
Integer minRankOrderIndex,
|
570
|
Integer maxRankOrderIndex) {
|
571
|
|
572
|
Map<TreeIndex, Integer> result = new HashMap<>();
|
573
|
if (treeIndexes == null || treeIndexes.isEmpty()){
|
574
|
return result;
|
575
|
}
|
576
|
|
577
|
String hql = " SELECT tn.treeIndex, r.orderIndex "
|
578
|
+ " FROM TaxonNode tn "
|
579
|
+ " JOIN tn.taxon t "
|
580
|
+ " JOIN t.name n "
|
581
|
+ " JOIN n.rank r "
|
582
|
+ " WHERE tn.treeIndex IN (:treeIndexes) ";
|
583
|
if (minRankOrderIndex != null){
|
584
|
hql += " AND r.orderIndex <= :minOrderIndex";
|
585
|
}
|
586
|
if (maxRankOrderIndex != null){
|
587
|
hql += " AND r.orderIndex >= :maxOrderIndex";
|
588
|
}
|
589
|
|
590
|
Query query = getSession().createQuery(hql);
|
591
|
query.setParameterList("treeIndexes", TreeIndex.toString(treeIndexes));
|
592
|
if (minRankOrderIndex != null){
|
593
|
query.setParameter("minOrderIndex", minRankOrderIndex);
|
594
|
}
|
595
|
if (maxRankOrderIndex != null){
|
596
|
query.setParameter("maxOrderIndex", maxRankOrderIndex);
|
597
|
}
|
598
|
|
599
|
@SuppressWarnings("unchecked")
|
600
|
List<Object[]> list = query.list();
|
601
|
for (Object[] o : list){
|
602
|
result.put(TreeIndex.NewInstance((String)o[0]), (Integer)o[1]);
|
603
|
}
|
604
|
return result;
|
605
|
}
|
606
|
|
607
|
@Override
|
608
|
public Map<TreeIndex, UuidAndTitleCache<?>> taxonUuidsForTreeIndexes(Collection<TreeIndex> treeIndexes) {
|
609
|
Map<TreeIndex, UuidAndTitleCache<?>> result = new HashMap<>();
|
610
|
if (treeIndexes == null || treeIndexes.isEmpty()){
|
611
|
return result;
|
612
|
}
|
613
|
|
614
|
String hql =
|
615
|
" SELECT tn.treeIndex, t.uuid, tnb.titleCache "
|
616
|
+ " FROM TaxonNode tn JOIN tn.taxon t Join t.name tnb "
|
617
|
+ " WHERE tn.treeIndex IN (:treeIndexes) ";
|
618
|
Query query = getSession().createQuery(hql);
|
619
|
query.setParameterList("treeIndexes", TreeIndex.toString(treeIndexes));
|
620
|
|
621
|
@SuppressWarnings("unchecked")
|
622
|
List<Object[]> list = query.list();
|
623
|
for (Object[] o : list){
|
624
|
result.put(TreeIndex.NewInstance((String)o[0]), new UuidAndTitleCache<>((UUID)o[1], null, (String)o[2]));
|
625
|
}
|
626
|
return result;
|
627
|
}
|
628
|
|
629
|
@Override
|
630
|
public List<TaxonNodeDto> getParentTaxonNodeDtoForRank(
|
631
|
Classification classification, Rank rank, TaxonBase<?> taxonBase) {
|
632
|
|
633
|
Taxon taxon = null;
|
634
|
if (taxonBase instanceof Taxon) {
|
635
|
taxon = CdmBase.deproxy(taxonBase, Taxon.class);
|
636
|
}else {
|
637
|
taxon = CdmBase.deproxy(((Synonym)taxonBase).getAcceptedTaxon());
|
638
|
}
|
639
|
TaxonNode node = null;
|
640
|
if (taxon != null) {
|
641
|
node = taxon.getTaxonNode(classification);
|
642
|
}
|
643
|
List<TaxonNodeDto> result = new ArrayList<>();
|
644
|
if (node != null) {
|
645
|
String treeIndex = node.treeIndex();
|
646
|
List<Integer> ancestorNodeIds = TreeIndex.NewInstance(treeIndex).parentNodeIds(false);
|
647
|
|
648
|
Criteria nodeCrit = getSession().createCriteria(TaxonNode.class);
|
649
|
Criteria taxonCrit = nodeCrit.createCriteria("taxon");
|
650
|
Criteria nameCrit = taxonCrit.createCriteria("name");
|
651
|
nodeCrit.add(Restrictions.in("id", ancestorNodeIds));
|
652
|
nodeCrit.add(Restrictions.eq("classification", classification));
|
653
|
nameCrit.add(Restrictions.eq("rank", rank));
|
654
|
|
655
|
@SuppressWarnings("unchecked")
|
656
|
List<TaxonNode> list = nodeCrit.list();
|
657
|
for (TaxonNode rankNode : list){
|
658
|
TaxonNodeDto dto = new TaxonNodeDto(rankNode);
|
659
|
result.add(dto);
|
660
|
}
|
661
|
}
|
662
|
return result;
|
663
|
}
|
664
|
|
665
|
|
666
|
@Override
|
667
|
public List<TaxonNodeDto> getParentTaxonNodeDtoForRank(
|
668
|
Classification classification, Rank rank, TaxonName name) {
|
669
|
|
670
|
Set<TaxonBase> taxa = name.getTaxonBases();
|
671
|
List<TaxonNodeDto> result = new ArrayList<>();
|
672
|
for (TaxonBase<?> taxonBase:taxa) {
|
673
|
List<TaxonNodeDto> tmpList = getParentTaxonNodeDtoForRank(classification, rank, taxonBase);
|
674
|
for (TaxonNodeDto tmpDto : tmpList){
|
675
|
boolean exists = false; //an equal method does not yet exist for TaxonNodeDto therefore this workaround
|
676
|
for (TaxonNodeDto dto: result){
|
677
|
if (dto.getTreeIndex().equals(tmpDto.getTreeIndex())){
|
678
|
exists = true;
|
679
|
}
|
680
|
}
|
681
|
if (!exists){
|
682
|
result.add(tmpDto);
|
683
|
}
|
684
|
}
|
685
|
}
|
686
|
return result;
|
687
|
}
|
688
|
|
689
|
@Override
|
690
|
public int countSecundumForSubtreeAcceptedTaxa(TreeIndex subTreeIndex, Reference newSec,
|
691
|
boolean overwriteExisting, boolean includeSharedTaxa, boolean emptySecundumDetail) {
|
692
|
String queryStr = forSubtreeAcceptedQueryStr(includeSharedTaxa, subTreeIndex, false, SelectMode.COUNT);
|
693
|
if (!overwriteExisting){
|
694
|
queryStr += " AND t.secSource.citation IS NULL ";
|
695
|
}
|
696
|
return countResult(queryStr);
|
697
|
}
|
698
|
|
699
|
private int countResult(String queryStr) {
|
700
|
Query query = getSession().createQuery(queryStr);
|
701
|
return ((Long)query.uniqueResult()).intValue();
|
702
|
}
|
703
|
|
704
|
@Override
|
705
|
public int countSecundumForSubtreeSynonyms(TreeIndex subTreeIndex, Reference newSec,
|
706
|
boolean overwriteExisting, boolean includeSharedTaxa, boolean emptySecundumDetail) {
|
707
|
String queryStr = forSubtreeSynonymQueryStr(includeSharedTaxa, subTreeIndex, false, SelectMode.COUNT);
|
708
|
if (!overwriteExisting){
|
709
|
queryStr += " AND syn.secSource.citation IS NULL ";
|
710
|
}
|
711
|
return countResult(queryStr);
|
712
|
}
|
713
|
|
714
|
@Override
|
715
|
public int countSecundumForSubtreeRelations(TreeIndex subTreeIndex, Reference newSec,
|
716
|
boolean overwriteExisting, boolean includeSharedTaxa, boolean emptySecundumDetail) {
|
717
|
String queryStr = forSubtreeRelationQueryStr(includeSharedTaxa, overwriteExisting, subTreeIndex, SelectMode.COUNT);
|
718
|
return countResult(queryStr);
|
719
|
}
|
720
|
|
721
|
//#3465
|
722
|
@Override
|
723
|
public Set<CdmBase> setSecundumForSubtreeAcceptedTaxa(TreeIndex subTreeIndex, Reference newSec,
|
724
|
boolean overwriteExisting, boolean includeSharedTaxa, boolean emptyDetail, IProgressMonitor monitor) {
|
725
|
//for some reason this does not work, maybe because the listeners are not activated,
|
726
|
//but also the first taxon for some reason does not get updated in terms of secundum, but only by the update listener
|
727
|
// String where = "SELECT t.id FROM TaxonNode tn JOIN tn.taxon t " +
|
728
|
// " WHERE tn.treeIndex like '%s%%' ORDER BY t.id";
|
729
|
// where = String.format(where, subTreeIndex.toString());
|
730
|
// Query query1 = getSession().createQuery(where);
|
731
|
// List l = query1.list();
|
732
|
//
|
733
|
// String hql = "UPDATE Taxon SET sec = :newSec, publish=false WHERE id IN (" + where + ")";
|
734
|
// Query query = getSession().createQuery(hql);
|
735
|
// query.setParameter("newSec", newSec);
|
736
|
// int n = query.executeUpdate();
|
737
|
|
738
|
String queryStr = forSubtreeAcceptedQueryStr(includeSharedTaxa, subTreeIndex, false, SelectMode.ID);
|
739
|
if (!overwriteExisting){
|
740
|
queryStr += " AND t.secSource.citation IS NULL ";
|
741
|
}
|
742
|
return setSecundum(newSec, emptyDetail, queryStr, monitor);
|
743
|
}
|
744
|
|
745
|
@Override
|
746
|
public Set<CdmBase> setSecundumForSubtreeSynonyms(TreeIndex subTreeIndex, Reference newSec,
|
747
|
boolean overwriteExisting, boolean includeSharedTaxa, boolean emptyDetail, IProgressMonitor monitor) {
|
748
|
|
749
|
String queryStr = forSubtreeSynonymQueryStr(includeSharedTaxa, subTreeIndex, false, SelectMode.ID);
|
750
|
if (!overwriteExisting){
|
751
|
queryStr += " AND syn.secSource.citation IS NULL ";
|
752
|
}
|
753
|
return setSecundum(newSec, emptyDetail, queryStr, monitor);
|
754
|
}
|
755
|
|
756
|
private <T extends TaxonBase<?>> Set<CdmBase> setSecundum(Reference newSec, boolean emptyDetail, String queryStr, IProgressMonitor monitor) {
|
757
|
Set<CdmBase> result = new HashSet<>();
|
758
|
Query query = getSession().createQuery(queryStr);
|
759
|
@SuppressWarnings("unchecked")
|
760
|
List<List<Integer>> partitionList = splitIdList(query.list(), DEFAULT_SET_SUBTREE_PARTITION_SIZE);
|
761
|
for (List<Integer> taxonIdList : partitionList){
|
762
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
763
|
List<T> taxonList = (List)taxonDao.loadList(taxonIdList, null, null);
|
764
|
for (T taxonBase : taxonList){
|
765
|
if (taxonBase != null){
|
766
|
taxonBase = CdmBase.deproxy(taxonBase);
|
767
|
SecundumSource secSourceBefore = taxonBase.getSecSource();
|
768
|
Reference refBefore = taxonBase.getSec();
|
769
|
String refDetailBefore = taxonBase.getSecMicroReference();
|
770
|
if (newSec == null && taxonBase.getSec() !=null
|
771
|
|| newSec != null && (taxonBase.getSec() == null || !newSec.equals(taxonBase.getSec()) )){
|
772
|
taxonBase.setSec(newSec);
|
773
|
}
|
774
|
if (emptyDetail){
|
775
|
if (taxonBase.getSecMicroReference() != null){
|
776
|
taxonBase.setSecMicroReference(null);
|
777
|
}
|
778
|
}
|
779
|
//compute updated objects
|
780
|
SecundumSource secSourceAfter = taxonBase.getSecSource();
|
781
|
|
782
|
if (!CdmUtils.nullSafeEqual(secSourceBefore, secSourceAfter)){
|
783
|
result.add(taxonBase);
|
784
|
//FIXME #9627 remove if fixed
|
785
|
result.add(taxonBase);
|
786
|
//EMXIF
|
787
|
}else if (secSourceBefore != null && secSourceBefore.equals(secSourceAfter)
|
788
|
&& (!CdmUtils.nullSafeEqual(refBefore, secSourceAfter.getCitation())
|
789
|
|| !CdmUtils.nullSafeEqual(refDetailBefore, secSourceAfter.getCitationMicroReference()))
|
790
|
){
|
791
|
result.add(secSourceBefore);
|
792
|
//FIXME #9627 remove if fixed
|
793
|
result.add(taxonBase);
|
794
|
//EMXIF
|
795
|
}
|
796
|
|
797
|
monitor.worked(1);
|
798
|
if (monitor.isCanceled()){
|
799
|
return result;
|
800
|
}
|
801
|
}
|
802
|
}
|
803
|
commitAndRestartTransaction(newSec);
|
804
|
monitor.worked(taxonIdList.size());
|
805
|
}
|
806
|
return result;
|
807
|
}
|
808
|
|
809
|
private void commitAndRestartTransaction(CdmBase... cdmBaseToUpdate) {
|
810
|
getSession().getTransaction().commit();
|
811
|
getSession().clear();
|
812
|
getSession().beginTransaction();
|
813
|
for (CdmBase cdmBase : cdmBaseToUpdate){
|
814
|
if (cdmBase != null){
|
815
|
getSession().update(cdmBase);
|
816
|
}
|
817
|
}
|
818
|
}
|
819
|
|
820
|
@Override
|
821
|
public Set<CdmBase> setSecundumForSubtreeRelations(TreeIndex subTreeIndex, Reference newRef,
|
822
|
Set<UUID> relationTypes, boolean overwriteExisting, boolean includeSharedTaxa, boolean emptyDetail, IProgressMonitor monitor) {
|
823
|
|
824
|
String queryStr = forSubtreeRelationQueryStr(includeSharedTaxa, overwriteExisting, subTreeIndex, SelectMode.ID);
|
825
|
|
826
|
Set<CdmBase> result = new HashSet<>();
|
827
|
Query query = getSession().createQuery(queryStr);
|
828
|
@SuppressWarnings("unchecked")
|
829
|
List<List<Integer>> partitionList = splitIdList(query.list(), DEFAULT_SET_SUBTREE_PARTITION_SIZE);
|
830
|
for (List<Integer> relIdList : partitionList){
|
831
|
List<TaxonRelationship> relList = taxonRelDao.loadList(relIdList, null, null);
|
832
|
for (TaxonRelationship rel : relList){
|
833
|
if (rel != null){
|
834
|
rel = CdmBase.deproxy(rel);
|
835
|
|
836
|
NamedSource sourceBefore = rel.getSource();
|
837
|
Reference refBefore = rel.getCitation();
|
838
|
String refDetailBefore = rel.getCitationMicroReference();
|
839
|
if (newRef == null && rel.getCitation() !=null
|
840
|
|| newRef != null && (rel.getCitation() == null || !newRef.equals(rel.getCitation()) )){
|
841
|
rel.setCitation(newRef);
|
842
|
}
|
843
|
if (emptyDetail){
|
844
|
if (rel.getCitationMicroReference() != null){
|
845
|
rel.setCitationMicroReference(null);
|
846
|
}
|
847
|
}
|
848
|
//compute updated objects
|
849
|
NamedSource sourceAfter = rel.getSource();
|
850
|
if (!CdmUtils.nullSafeEqual(sourceBefore, sourceAfter)){
|
851
|
result.add(rel);
|
852
|
//FIXME #9627 remove if fixed
|
853
|
result.add(rel.getToTaxon());
|
854
|
//EMXIF
|
855
|
|
856
|
}else if (sourceBefore != null && sourceBefore.equals(sourceAfter)
|
857
|
&& (!CdmUtils.nullSafeEqual(refBefore, sourceAfter.getCitation())
|
858
|
|| !CdmUtils.nullSafeEqual(refDetailBefore,sourceAfter.getCitationMicroReference()))
|
859
|
){
|
860
|
result.add(sourceBefore);
|
861
|
//FIXME #9627 remove if fixed
|
862
|
result.add(rel.getToTaxon());
|
863
|
//EMXIF
|
864
|
}
|
865
|
|
866
|
monitor.worked(1);
|
867
|
if (monitor.isCanceled()){
|
868
|
return result;
|
869
|
}
|
870
|
}
|
871
|
}
|
872
|
commitAndRestartTransaction();
|
873
|
monitor.worked(relList.size());
|
874
|
}
|
875
|
|
876
|
return result;
|
877
|
}
|
878
|
|
879
|
private List<List<Integer>> splitIdList(List<Integer> idList, Integer size){
|
880
|
List<List<Integer>> result = new ArrayList<>();
|
881
|
for (int i = 0; (i*size)<idList.size(); i++) {
|
882
|
int upper = Math.min((i+1)*size, idList.size());
|
883
|
result.add(idList.subList(i*size, upper));
|
884
|
}
|
885
|
return result;
|
886
|
}
|
887
|
|
888
|
@Override
|
889
|
public int countPublishForSubtreeAcceptedTaxa(TreeIndex subTreeIndex, boolean publish, boolean includeSharedTaxa, boolean includeHybrids) {
|
890
|
String queryStr = forSubtreeAcceptedQueryStr(includeSharedTaxa, subTreeIndex, !includeHybrids, SelectMode.COUNT);
|
891
|
queryStr += " AND t.publish != :publish ";
|
892
|
Query query = getSession().createQuery(queryStr);
|
893
|
query.setBoolean("publish", publish);
|
894
|
return ((Long)query.uniqueResult()).intValue();
|
895
|
}
|
896
|
|
897
|
@Override
|
898
|
public int countPublishForSubtreeSynonyms(TreeIndex subTreeIndex, boolean publish, boolean includeSharedTaxa, boolean includeHybrids) {
|
899
|
String queryStr = forSubtreeSynonymQueryStr(includeSharedTaxa, subTreeIndex, !includeHybrids, SelectMode.COUNT);
|
900
|
queryStr += " AND syn.publish != :publish ";
|
901
|
Query query = getSession().createQuery(queryStr);
|
902
|
query.setBoolean("publish", publish);
|
903
|
return ((Long)query.uniqueResult()).intValue();
|
904
|
}
|
905
|
|
906
|
@Override
|
907
|
public Set<TaxonBase> setPublishForSubtreeAcceptedTaxa(TreeIndex subTreeIndex, boolean publish,
|
908
|
boolean includeSharedTaxa, boolean includeHybrids, IProgressMonitor monitor) {
|
909
|
String queryStr = forSubtreeAcceptedQueryStr(includeSharedTaxa, subTreeIndex, !includeHybrids, SelectMode.ID);
|
910
|
queryStr += " AND t.publish != :publish ";
|
911
|
return setPublish(publish, queryStr, null, monitor);
|
912
|
}
|
913
|
|
914
|
@Override
|
915
|
public Set<TaxonBase> setPublishForSubtreeSynonyms(TreeIndex subTreeIndex, boolean publish,
|
916
|
boolean includeSharedTaxa, boolean includeHybrids, IProgressMonitor monitor) {
|
917
|
String queryStr = forSubtreeSynonymQueryStr(includeSharedTaxa, subTreeIndex, !includeHybrids, SelectMode.ID);
|
918
|
queryStr += " AND syn.publish != :publish ";
|
919
|
return setPublish(publish, queryStr, null, monitor);
|
920
|
}
|
921
|
|
922
|
@Override
|
923
|
public int countPublishForSubtreeRelatedTaxa(TreeIndex subTreeIndex, boolean publish, boolean includeSharedTaxa, boolean includeHybrids) {
|
924
|
String queryStr = forSubtreeRelatedTaxaQueryStr(includeSharedTaxa, subTreeIndex, !includeHybrids, SelectMode.COUNT);
|
925
|
queryStr += " AND relTax.publish != :publish ";
|
926
|
Query query = getSession().createQuery(queryStr);
|
927
|
query.setBoolean("publish", publish);
|
928
|
return ((Long)query.uniqueResult()).intValue();
|
929
|
}
|
930
|
|
931
|
@Override
|
932
|
public Set<TaxonBase> setPublishForSubtreeRelatedTaxa(TreeIndex subTreeIndex, boolean publish,
|
933
|
Set<UUID> relationTypes, boolean includeSharedTaxa, boolean includeHybrids,
|
934
|
IProgressMonitor monitor) {
|
935
|
String queryStr = forSubtreeRelatedTaxaQueryStr(includeSharedTaxa, subTreeIndex, !includeHybrids, SelectMode.ID);
|
936
|
queryStr += " AND relTax.publish != :publish ";
|
937
|
queryStr += " AND rel.type.uuid IN (:relTypeUuid)";
|
938
|
return setPublish(publish, queryStr, relationTypes, monitor);
|
939
|
}
|
940
|
|
941
|
private <T extends TaxonBase<?>> Set<T> setPublish(boolean publish, String queryStr, Set<UUID> relTypeUuids, IProgressMonitor monitor) {
|
942
|
Set<T> result = new HashSet<>();
|
943
|
Query query = getSession().createQuery(queryStr);
|
944
|
query.setBoolean("publish", publish);
|
945
|
if (relTypeUuids != null && !relTypeUuids.isEmpty()){
|
946
|
query.setParameterList("relTypeUuid", relTypeUuids);
|
947
|
}
|
948
|
@SuppressWarnings("unchecked")
|
949
|
List<List<Integer>> partitionList = splitIdList(query.list(), DEFAULT_SET_SUBTREE_PARTITION_SIZE);
|
950
|
for (List<Integer> taxonIdList : partitionList){
|
951
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
952
|
List<T> taxonList = (List)taxonDao.loadList(taxonIdList, null, null);
|
953
|
for (T taxonBase : taxonList){
|
954
|
if (taxonBase != null){
|
955
|
if (taxonBase.isPublish() != publish){ //to be on the save side
|
956
|
taxonBase.setPublish(publish);
|
957
|
result.add(CdmBase.deproxy(taxonBase));
|
958
|
}
|
959
|
monitor.worked(1);
|
960
|
if (monitor.isCanceled()){
|
961
|
return result;
|
962
|
}
|
963
|
}
|
964
|
}
|
965
|
commitAndRestartTransaction();
|
966
|
}
|
967
|
return result;
|
968
|
}
|
969
|
|
970
|
private String forSubtreeSynonymQueryStr(boolean includeSharedTaxa, TreeIndex subTreeIndex, boolean excludeHybrids, SelectMode mode) {
|
971
|
String queryStr = "SELECT " + mode.hql("syn")
|
972
|
+ " FROM TaxonNode tn "
|
973
|
+ " JOIN tn.taxon t "
|
974
|
+ " JOIN t.synonyms syn "
|
975
|
+ " LEFT JOIN syn.name n "
|
976
|
+ " LEFT JOIN syn.secSource ss ";
|
977
|
String whereStr = " tn.treeIndex LIKE '%1$s%%' ";
|
978
|
if (!includeSharedTaxa){
|
979
|
whereStr += " AND NOT EXISTS ("
|
980
|
+ "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
|
981
|
}
|
982
|
whereStr = handleExcludeHybrids(whereStr, excludeHybrids, "syn");
|
983
|
queryStr += " WHERE " + String.format(whereStr, subTreeIndex.toString());
|
984
|
|
985
|
return queryStr;
|
986
|
}
|
987
|
|
988
|
private String handleExcludeHybrids(String whereStr, boolean excludeHybrids, String t) {
|
989
|
if(excludeHybrids){
|
990
|
|
991
|
String hybridWhere = " AND (n is NULL OR "
|
992
|
+ " (n.monomHybrid=0 AND n.binomHybrid=0 "
|
993
|
+ " AND n.trinomHybrid=0 AND n.hybridFormula=0 )) ";
|
994
|
|
995
|
whereStr += hybridWhere; //String.format(hybridWhere, t);
|
996
|
}
|
997
|
return whereStr;
|
998
|
}
|
999
|
|
1000
|
private String forSubtreeRelatedTaxaQueryStr(boolean includeSharedTaxa, TreeIndex subTreeIndex,
|
1001
|
boolean excludeHybrids, SelectMode mode) {
|
1002
|
String queryStr = "SELECT " + mode.hql("relTax")
|
1003
|
+ " FROM TaxonNode tn "
|
1004
|
+ " JOIN tn.taxon t "
|
1005
|
+ " JOIN t.relationsToThisTaxon rel"
|
1006
|
+ " JOIN rel.relatedFrom relTax "
|
1007
|
+ " LEFT JOIN relTax.name n ";
|
1008
|
String whereStr =" tn.treeIndex LIKE '%1$s%%' ";
|
1009
|
if (!includeSharedTaxa){
|
1010
|
//toTaxon should only be used in the given subtree
|
1011
|
whereStr += " AND NOT EXISTS ("
|
1012
|
+ "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
|
1013
|
//from taxon should not be used in another classification
|
1014
|
whereStr += " AND NOT EXISTS ("
|
1015
|
+ "FROM TaxonNode tn3 WHERE tn3.taxon = relTax AND tn3.treeIndex not like '%1$s%%') ";
|
1016
|
//fromTaxon should not be related as e.g. pro parte synonym or misapplication to
|
1017
|
//another taxon which is not part of the subtree
|
1018
|
//TODO and has not the publish state
|
1019
|
whereStr += " AND NOT EXISTS ("
|
1020
|
+ "FROM TaxonNode tn4 JOIN tn4.taxon t2 JOIN t2.relationsToThisTaxon rel2 "
|
1021
|
+ " WHERE rel2.relatedFrom = relTax AND tn4.treeIndex not like '%1$s%%' "
|
1022
|
+ " AND tn4.taxon.publish != :publish ) ";
|
1023
|
}
|
1024
|
whereStr = handleExcludeHybrids(whereStr, excludeHybrids, "relTax");
|
1025
|
queryStr += " WHERE " + String.format(whereStr, subTreeIndex.toString());
|
1026
|
|
1027
|
return queryStr;
|
1028
|
}
|
1029
|
|
1030
|
/**
|
1031
|
* query for
|
1032
|
*/
|
1033
|
private String forSubtreeRelationQueryStr(boolean includeSharedTaxa, boolean overwriteExisting,
|
1034
|
TreeIndex subTreeIndex, SelectMode mode) {
|
1035
|
|
1036
|
String queryStr = "SELECT " + mode.hql("rel")
|
1037
|
+ " FROM TaxonNode tn "
|
1038
|
+ " JOIN tn.taxon t "
|
1039
|
+ " JOIN t.relationsToThisTaxon rel "
|
1040
|
+ " LEFT JOIN rel.source src ";
|
1041
|
String whereStr =" tn.treeIndex LIKE '%1$s%%' ";
|
1042
|
if (!includeSharedTaxa){
|
1043
|
//toTaxon should only be used in the given subtree
|
1044
|
whereStr += " AND NOT EXISTS ("
|
1045
|
+ "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
|
1046
|
}
|
1047
|
queryStr += " WHERE " + String.format(whereStr, subTreeIndex.toString());
|
1048
|
if (!overwriteExisting){
|
1049
|
queryStr += " AND (rel.source IS NULL OR src.citation IS NULL) ";
|
1050
|
}
|
1051
|
|
1052
|
return queryStr;
|
1053
|
}
|
1054
|
|
1055
|
private enum SelectMode{
|
1056
|
COUNT(" count(*) "),
|
1057
|
ID ("id "),
|
1058
|
UUID("uuid "),
|
1059
|
FULL("");
|
1060
|
private String hql;
|
1061
|
SelectMode(String hql){
|
1062
|
this.hql = hql;
|
1063
|
}
|
1064
|
public String hql(String prefix){
|
1065
|
switch (this){
|
1066
|
case ID:
|
1067
|
case UUID:
|
1068
|
return CdmUtils.Nz(prefix)+"." + hql;
|
1069
|
case FULL:
|
1070
|
return CdmUtils.Nz(prefix) + hql;
|
1071
|
case COUNT:
|
1072
|
default: return hql;
|
1073
|
}
|
1074
|
|
1075
|
}
|
1076
|
}
|
1077
|
|
1078
|
private String forSubtreeAcceptedQueryStr(boolean includeSharedTaxa, TreeIndex subTreeIndex, boolean excludeHybrids, SelectMode mode) {
|
1079
|
String queryStr = "SELECT " + mode.hql("t")
|
1080
|
+ " FROM TaxonNode tn "
|
1081
|
+ " JOIN tn.taxon t "
|
1082
|
+ " LEFT JOIN t.name n "
|
1083
|
+ " LEFT JOIN t.secSource ss ";
|
1084
|
String whereStr = " tn.treeIndex like '%1$s%%' ";
|
1085
|
if (!includeSharedTaxa){
|
1086
|
whereStr += " AND NOT EXISTS ("
|
1087
|
+ "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
|
1088
|
}
|
1089
|
whereStr = handleExcludeHybrids(whereStr, excludeHybrids, "t");
|
1090
|
queryStr += " WHERE " + String.format(whereStr, subTreeIndex.toString());
|
1091
|
|
1092
|
return queryStr;
|
1093
|
}
|
1094
|
|
1095
|
@Override
|
1096
|
public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, Integer limit, String pattern, boolean searchForClassifications, boolean includeDoubtful) {
|
1097
|
|
1098
|
Query query = createQueryForUuidAndTitleCache(limit, classification.getUuid(), pattern, includeDoubtful);
|
1099
|
@SuppressWarnings("unchecked")
|
1100
|
List<SortableTaxonNodeQueryResult> result = query.list();
|
1101
|
|
1102
|
|
1103
|
if (searchForClassifications){
|
1104
|
String queryString = "SELECT new " + SortableTaxonNodeQueryResult.class.getName() + "("
|
1105
|
+ " node.uuid, node.id, node.classification.titleCache"
|
1106
|
+ ") "
|
1107
|
+ " FROM TaxonNode AS node "
|
1108
|
+ " WHERE node.classification.id = " + classification.getId() +
|
1109
|
" AND node.taxon IS NULL";
|
1110
|
if (pattern != null){
|
1111
|
if (pattern.equals("?")){
|
1112
|
limit = null;
|
1113
|
} else{
|
1114
|
if (!pattern.endsWith("*")){
|
1115
|
pattern += "%";
|
1116
|
}
|
1117
|
pattern = pattern.replace("*", "%");
|
1118
|
pattern = pattern.replace("?", "%");
|
1119
|
queryString = queryString + " AND node.classification.titleCache LIKE (:pattern) " ;
|
1120
|
}
|
1121
|
}
|
1122
|
query = getSession().createQuery(queryString);
|
1123
|
|
1124
|
if (limit != null){
|
1125
|
query.setMaxResults(limit);
|
1126
|
}
|
1127
|
|
1128
|
if (pattern != null && !pattern.equals("?")){
|
1129
|
query.setParameter("pattern", pattern);
|
1130
|
}
|
1131
|
@SuppressWarnings("unchecked")
|
1132
|
List<SortableTaxonNodeQueryResult> resultClassifications = query.list();
|
1133
|
|
1134
|
result.addAll(resultClassifications);
|
1135
|
}
|
1136
|
|
1137
|
if(result.size() == 0){
|
1138
|
return null;
|
1139
|
}else{
|
1140
|
List<UuidAndTitleCache<TaxonNode>> list = new ArrayList<>(result.size());
|
1141
|
Collections.sort(result, new SortableTaxonNodeQueryResultComparator());
|
1142
|
for (SortableTaxonNodeQueryResult resultDTO : result){
|
1143
|
list.add(new UuidAndTitleCache<>(TaxonNode.class, resultDTO.getTaxonNodeUuid(), resultDTO.getTaxonNodeId(), resultDTO.getTaxonTitleCache()));
|
1144
|
}
|
1145
|
|
1146
|
return list;
|
1147
|
}
|
1148
|
}
|
1149
|
|
1150
|
@Override
|
1151
|
public List<TaxonNodeDto> getTaxonNodeDto(Integer limit, String pattern, UUID classificationUuid) {
|
1152
|
String queryString = "SELECT new " + SortableTaxonNodeQueryResult.class.getName() + "("
|
1153
|
+ "tn.uuid, tn.id, t.titleCache, rank "
|
1154
|
+ ") "
|
1155
|
+ " FROM TaxonNode tn "
|
1156
|
+ " INNER JOIN tn.taxon AS t "
|
1157
|
+ " INNER JOIN tn.classification AS cls "
|
1158
|
+ " INNER JOIN t.name AS name "
|
1159
|
+ " LEFT OUTER JOIN name.rank AS rank "
|
1160
|
+ " WHERE t.titleCache LIKE :pattern ";
|
1161
|
if(classificationUuid != null){
|
1162
|
queryString += "AND cls.uuid = :classificationUuid";
|
1163
|
}
|
1164
|
|
1165
|
Query query = getSession().createQuery(queryString);
|
1166
|
|
1167
|
query.setParameter("pattern", pattern.toLowerCase()+"%");
|
1168
|
if(classificationUuid != null){
|
1169
|
query.setParameter("classificationUuid", classificationUuid);
|
1170
|
}
|
1171
|
|
1172
|
@SuppressWarnings("unchecked")
|
1173
|
List<SortableTaxonNodeQueryResult> result = query.list();
|
1174
|
Collections.sort(result, new SortableTaxonNodeQueryResultComparator());
|
1175
|
|
1176
|
List<TaxonNodeDto> list = new ArrayList<>();
|
1177
|
for(SortableTaxonNodeQueryResult queryDTO : result){
|
1178
|
list.add(new TaxonNodeDto(queryDTO.getTaxonNodeUuid(), queryDTO.getTaxonNodeId(), queryDTO.getTaxonTitleCache()));
|
1179
|
}
|
1180
|
return list;
|
1181
|
}
|
1182
|
|
1183
|
@Override
|
1184
|
public List<TaxonNodeDto> getUuidAndTitleCache(Integer limit, String pattern, UUID classificationUuid) {
|
1185
|
return getUuidAndTitleCache(limit, pattern, classificationUuid, false);
|
1186
|
}
|
1187
|
|
1188
|
@Override
|
1189
|
public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
|
1190
|
Classification classification, Integer limit, String pattern, boolean searchForClassifications) {
|
1191
|
return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification, limit, pattern, searchForClassifications, false);
|
1192
|
}
|
1193
|
|
1194
|
}
|