Project

General

Profile

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

    
10
package eu.etaxonomy.cdm.persistence.dao.hibernate.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.HashMap;
17
import java.util.HashSet;
18
import java.util.Iterator;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Set;
22
import java.util.UUID;
23

    
24
import org.apache.log4j.Logger;
25
import org.hibernate.Criteria;
26
import org.hibernate.Hibernate;
27
import org.hibernate.Query;
28
import org.hibernate.criterion.Projections;
29
import org.hibernate.criterion.Restrictions;
30
import org.springframework.beans.factory.annotation.Autowired;
31
import org.springframework.beans.factory.annotation.Qualifier;
32
import org.springframework.stereotype.Repository;
33

    
34
import eu.etaxonomy.cdm.common.CdmUtils;
35
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
36
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
37
import eu.etaxonomy.cdm.model.common.CdmBase;
38
import eu.etaxonomy.cdm.model.common.TreeIndex;
39
import eu.etaxonomy.cdm.model.reference.Reference;
40
import eu.etaxonomy.cdm.model.taxon.Classification;
41
import eu.etaxonomy.cdm.model.taxon.Taxon;
42
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
43
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
44
import eu.etaxonomy.cdm.model.taxon.TaxonNodeAgentRelation;
45
import eu.etaxonomy.cdm.model.taxon.UuidAndTitleCacheTaxonComparator;
46
import eu.etaxonomy.cdm.persistence.dao.hibernate.common.AnnotatableDaoImpl;
47
import eu.etaxonomy.cdm.persistence.dao.taxon.IClassificationDao;
48
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonDao;
49
import eu.etaxonomy.cdm.persistence.dao.taxon.ITaxonNodeDao;
50
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
51
import eu.etaxonomy.cdm.persistence.dto.UuidAndTitleCache;
52

    
53
/**
54
 * @author a.mueller
55
 * @since 16.06.2009
56
 */
57
@Repository
58
@Qualifier("taxonNodeDaoHibernateImpl")
59
public class TaxonNodeDaoHibernateImpl extends AnnotatableDaoImpl<TaxonNode>
60
		implements ITaxonNodeDao {
61
	@SuppressWarnings("unused")
62
	private static final Logger logger = Logger.getLogger(TaxonNodeDaoHibernateImpl.class);
63

    
64
	@Autowired
65
	private ITaxonDao taxonDao;
66
	@Autowired
67
	private IClassificationDao classificationDao;
68

    
69
	public TaxonNodeDaoHibernateImpl() {
70
		super(TaxonNode.class);
71
	}
72
	@Override
73
	public UUID delete(TaxonNode persistentObject, boolean deleteChildren){
74
		Taxon taxon = persistentObject.getTaxon();
75
		taxon = HibernateProxyHelper.deproxy(taxon);
76

    
77
		/*Session session = this.getSession();
78
		Query query = session.createQuery("from TaxonNode t where t.taxon = :taxon");
79
		query.setParameter("taxon", taxon);
80
		List result = query.list();*/
81
		if (taxon != null){
82
		    Hibernate.initialize(taxon);
83
		    Hibernate.initialize(taxon.getTaxonNodes());
84
			Set<TaxonNode> nodes = taxon.getTaxonNodes();
85
			//Hibernate.initialize(taxon.getTaxonNodes());
86
			for (TaxonNode node:nodes) {
87
			    node = HibernateProxyHelper.deproxy(node);
88

    
89
			    if (node.equals(persistentObject)){
90
			        if (node.hasChildNodes()){
91
			            Iterator<TaxonNode> childNodes = node.getChildNodes().iterator();
92
			            TaxonNode childNode;
93
			            List<TaxonNode> listForDeletion = new ArrayList<>();
94
	                    while (childNodes.hasNext()){
95
	                        childNode = childNodes.next();
96
	                        listForDeletion.add(childNode);
97
	                        childNodes.remove();
98

    
99
	                    }
100
	                    for (TaxonNode deleteNode:listForDeletion){
101
	                        delete(deleteNode, deleteChildren);
102
	                    }
103
	                }
104

    
105
			        taxon.removeTaxonNode(node, deleteChildren);
106
			        taxonDao.saveOrUpdate(taxon);
107
    				taxon = HibernateProxyHelper.deproxy(taxonDao.findByUuid(taxon.getUuid()), Taxon.class);
108
    				taxonDao.delete(taxon);
109

    
110
			    }
111
			}
112
		}
113

    
114
		UUID result = super.delete(persistentObject);
115

    
116
		return result;
117
	}
118

    
119
	@Override
120
	public List<TaxonNode> getTaxonOfAcceptedTaxaByClassification(Classification classification, Integer start, Integer end) {
121
		int classificationId = classification.getId();
122
		String limit = "";
123
		if(start !=null && end != null){
124
		    limit = "LIMIT "+start+"," +end;
125
		}
126
		//FIXME write test
127
        String queryString = "SELECT DISTINCT nodes.*,taxa.titleCache "
128
                + " FROM TaxonNode AS nodes "
129
                + "    LEFT JOIN TaxonBase AS taxa ON nodes.taxon_id = taxa.id "
130
                + " WHERE taxa.DTYPE = 'Taxon' "
131
                + "    AND nodes.classification_id = " + classificationId +
132
                  " ORDER BY taxa.titleCache " + limit;
133
        @SuppressWarnings("unchecked")
134
        List<TaxonNode> result  = getSession().createSQLQuery(queryString).addEntity(TaxonNode.class).list();
135

    
136
       return result;
137
	}
138

    
139
    @Override
140
    public int countTaxonOfAcceptedTaxaByClassification(Classification classification){
141
        int classificationId = classification.getId();
142
        //FIXME write test
143
        String queryString = ""
144
                + " SELECT DISTINCT COUNT('nodes.*') "
145
                + " FROM TaxonNode AS nodes "
146
                + "   LEFT JOIN TaxonBase AS taxa ON nodes.taxon_id = taxa.id "
147
                + " WHERE taxa.DTYPE = 'Taxon' AND nodes.classification_id = " + classificationId;
148
         @SuppressWarnings("unchecked")
149
        List<BigInteger> result = getSession().createSQLQuery(queryString).list();
150
         return result.get(0).intValue ();
151
    }
152

    
153
    @Override
154
    public List<UuidAndTitleCache<TaxonNode>> listChildNodesAsUuidAndTitleCache(UuidAndTitleCache<TaxonNode> parent) {
155
        String queryString =
156
                  " SELECT tn.uuid, tn.id, t.titleCache "
157
                + " FROM TaxonNode tn "
158
                + "    INNER JOIN tn.taxon AS t "
159
                + " WHERE tn.parent.uuid = :parentId";
160

    
161
        Query query =  getSession().createQuery(queryString);
162
        query.setParameter("parentId", parent.getUuid());
163

    
164
        @SuppressWarnings("unchecked")
165
        List<Object[]> result = query.list();
166

    
167
        List<UuidAndTitleCache<TaxonNode>> list = new ArrayList<>();
168
        for(Object[] object : result){
169
            list.add(new UuidAndTitleCache<>((UUID) object[0],(Integer) object[1], (String) object[2]));
170
        }
171
        return list;
172
    }
173

    
174
    @Override
175
    public List<TaxonNodeDto> listChildNodesAsTaxonNodeDto(UuidAndTitleCache<TaxonNode> parent) {
176
        String queryString =
177
                 " SELECT tn "
178
               + " FROM TaxonNode tn "
179
               + "    INNER JOIN tn.taxon AS t "
180
               + " WHERE tn.parent.uuid = :parentId";
181
        Query query =  getSession().createQuery(queryString);
182
        query.setParameter("parentId", parent.getUuid());
183

    
184
        @SuppressWarnings("unchecked")
185
        List<TaxonNode> result = query.list();
186

    
187
        List<TaxonNodeDto> list = new ArrayList<>();
188
        for(TaxonNode object : result){
189
            list.add(new TaxonNodeDto(object));
190
        }
191
        return list;
192
    }
193

    
194
    @Override
195
    public List<UuidAndTitleCache<TaxonNode>> getUuidAndTitleCache(Integer limit, String pattern, UUID classificationUuid) {
196
        String queryString = "SELECT tn.uuid, tn.id, t.titleCache, t.name.rank "
197
                + " FROM TaxonNode tn "
198
        		+ "   INNER JOIN tn.taxon AS t "
199
        		+ "   INNER JOIN tn.classification AS cls "
200
        		+ "WHERE t.titleCache LIKE :pattern ";
201
        if(classificationUuid != null){
202
        	queryString += "AND cls.uuid = :classificationUuid";
203
        }
204
        Query query =  getSession().createQuery(queryString);
205

    
206
        query.setParameter("pattern", pattern.toLowerCase()+"%");
207
        query.setParameter("classificationUuid", classificationUuid);
208

    
209

    
210
        @SuppressWarnings("unchecked")
211
        List<Object[]> result = query.list();
212
        Collections.sort(result, new UuidAndTitleCacheTaxonComparator());
213

    
214
        List<UuidAndTitleCache<TaxonNode>> list = new ArrayList<>();
215
        for(Object[] object : result){
216
            list.add(new UuidAndTitleCache<>((UUID) object[0],(Integer) object[1], (String) object[2]));
217
        }
218
        return list;
219
    }
220

    
221
    /**
222
     * {@inheritDoc}
223
     */
224
    @Override
225
    public UuidAndTitleCache<TaxonNode> getParentUuidAndTitleCache(UuidAndTitleCache<TaxonNode> child) {
226
        String queryString = ""
227
                + " SELECT tn.parent.uuid, tn.parent.id, tn.parent.taxon.titleCache, "
228
                + "                  tn.parent.classification.titleCache "
229
                + " FROM TaxonNode tn"
230
                + "    LEFT OUTER JOIN tn.parent.taxon"
231
                + " WHERE tn.id = :childId";
232
        Query query =  getSession().createQuery(queryString);
233
        query.setParameter("childId", child.getId());
234
        List<UuidAndTitleCache<TaxonNode>> list = new ArrayList<>();
235

    
236
        @SuppressWarnings("unchecked")
237
        List<Object[]> result = query.list();
238

    
239
        for(Object[] object : result){
240
            UUID uuid = (UUID) object[0];
241
            Integer id = (Integer) object[1];
242
            String taxonTitleCache = (String) object[2];
243
            String classificationTitleCache = (String) object[3];
244
            if(taxonTitleCache!=null){
245
                list.add(new UuidAndTitleCache<>(uuid,id, taxonTitleCache));
246
            }
247
            else{
248
                list.add(new UuidAndTitleCache<>(uuid,id, classificationTitleCache));
249
            }
250
        }
251
        if(list.size()==1){
252
            return list.iterator().next();
253
        }
254
        return null;
255
    }
256

    
257
    @Override
258
    public List<TaxonNode> listChildrenOf(TaxonNode node, Integer pageSize, Integer pageIndex,
259
            boolean recursive, boolean includeUnpublished, List<String> propertyPaths){
260

    
261
        if (recursive == true){
262
    		Criteria crit = childrenOfCriteria(node, includeUnpublished);
263

    
264
    		if(pageSize != null) {
265
                crit.setMaxResults(pageSize);
266
                if(pageIndex != null) {
267
                    crit.setFirstResult(pageIndex * pageSize);
268
                } else {
269
                    crit.setFirstResult(0);
270
                }
271
            }
272
    		@SuppressWarnings("unchecked")
273
            List<TaxonNode> results = crit.list();
274
    		results.remove(node);
275
    		defaultBeanInitializer.initializeAll(results, propertyPaths);
276
    		return results;
277
    	}else{
278
    		return classificationDao.listChildrenOf(node.getTaxon(), node.getClassification(), null,
279
    		       includeUnpublished, pageSize, pageIndex, propertyPaths);
280
    	}
281

    
282
    }
283

    
284
    @Override
285
	public Long countChildrenOf(TaxonNode node, Classification classification,
286
			boolean recursive, boolean includeUnpublished) {
287

    
288
		if (recursive == true){
289
			Criteria crit = childrenOfCriteria(node, includeUnpublished);
290
    		crit.setProjection(Projections.rowCount());
291
    		return ((Integer)crit.uniqueResult().hashCode()).longValue();
292
		}else{
293
			return classificationDao.countChildrenOf(
294
			        node.getTaxon(), classification, null, includeUnpublished);
295
		}
296
	}
297
    /**
298
     * @param node
299
     * @param includeUnpublished
300
     * @return
301
     */
302
    private Criteria childrenOfCriteria(TaxonNode node, boolean includeUnpublished) {
303
        Criteria crit = getSession().createCriteria(TaxonNode.class);
304
        crit.add( Restrictions.like("treeIndex", node.treeIndex()+ "%") );
305
        if (!includeUnpublished){
306
            crit.createCriteria("taxon").add( Restrictions.eq("publish", Boolean.TRUE));
307
        }
308
        return crit;
309
    }
310
    /**
311
     * {@inheritDoc}
312
     */
313
    @Override
314
    public List<TaxonNodeAgentRelation> listTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid,
315
            UUID agentUuid, UUID rankUuid, UUID relTypeUuid, Integer start, Integer limit,
316
            List<String> propertyPaths) {
317

    
318
        StringBuilder hql = prepareListTaxonNodeAgentRelations(taxonUuid, classificationUuid,
319
                agentUuid, rankUuid, relTypeUuid, false);
320

    
321
        Query query =  getSession().createQuery(hql.toString());
322

    
323
        if(limit != null) {
324
            query.setMaxResults(limit);
325
            if(start != null) {
326
                query.setFirstResult(start);
327
            }
328
        }
329

    
330
        setParamsForListTaxonNodeAgentRelations(taxonUuid, classificationUuid, agentUuid, rankUuid, relTypeUuid, query);
331

    
332
        @SuppressWarnings("unchecked")
333
        List<TaxonNodeAgentRelation> records = query.list();
334

    
335
        if(propertyPaths != null) {
336
            defaultBeanInitializer.initializeAll(records, propertyPaths);
337
        }
338
        return records;
339
    }
340

    
341
    /**
342
     * {@inheritDoc}
343
     */
344
    @Override
345
    public long countTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid, UUID agentUuid, UUID rankUuid, UUID relTypeUuid) {
346

    
347
        StringBuilder hql = prepareListTaxonNodeAgentRelations(taxonUuid, classificationUuid, agentUuid, rankUuid, relTypeUuid, true);
348
        Query query =  getSession().createQuery(hql.toString());
349

    
350
        setParamsForListTaxonNodeAgentRelations(taxonUuid, classificationUuid, agentUuid, rankUuid, relTypeUuid, query);
351

    
352
        Long count = Long.parseLong(query.uniqueResult().toString());
353

    
354
        return count;
355
    }
356
    /**
357
     * @param taxonUuid
358
     * @param classificationUuid
359
     * @param agentUuid
360
     * @param relTypeUuid TODO
361
     * @param doCount TODO
362
     * @param rankId
363
     *     limit to taxa having this rank, only applies if <code>taxonUuid = null</code>
364
     * @return
365
     */
366
    private StringBuilder prepareListTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid, UUID agentUuid, UUID rankUuid, UUID relTypeUuid, boolean doCount) {
367

    
368
        StringBuilder hql = new StringBuilder();
369

    
370
        String join_fetch_mode = doCount ? "JOIN" : "JOIN FETCH";
371

    
372
        if(doCount) {
373
            hql.append("SELECT COUNT(tnar)");
374
        } else {
375
            hql.append("SELECT tnar");
376
        }
377

    
378
        hql.append(" FROM TaxonNodeAgentRelation AS tnar ");
379
        if(taxonUuid != null) {
380
            // taxonUuid is search filter, do not fetch it
381
            hql.append(" JOIN tnar.taxonNode AS tn "
382
                    + "  JOIN tn.taxon AS t ");
383
        } else {
384
            hql.append(join_fetch_mode)
385
                .append(" tnar.taxonNode AS tn ")
386
                .append(join_fetch_mode).append(" tn.taxon AS t ");
387
            if(rankUuid != null) {
388
                hql.append(" join t.name as n ");
389
            }
390
        }
391
        hql.append(" JOIN tn.classification AS c ");
392
        if(agentUuid != null) {
393
            // agentUuid is search filter, do not fetch it
394
//            hql.append(" join tnar.agent as a ");
395
            hql.append(join_fetch_mode).append(" tnar.agent AS a ");
396
        } else {
397
            hql.append(join_fetch_mode).append(" tnar.agent AS a ");
398
        }
399

    
400
        hql.append(" WHERE (1 = 1) ");
401

    
402
        if(relTypeUuid != null) {
403
            hql.append(" AND tnar.type.uuid = :relTypeUuid ");
404
        }
405

    
406
        if(taxonUuid != null) {
407
            hql.append(" AND t.uuid = :taxonUuid ");
408
        } else {
409
            if(rankUuid != null) {
410
                hql.append(" AND n.rank.uuid = :rankUuid ");
411
            }
412
        }
413
        if(classificationUuid != null) {
414
            hql.append(" AND c.uuid = :classificationUuid ");
415
        }
416
        if(agentUuid != null) {
417
            hql.append(" AND a.uuid = :agentUuid ");
418
        }
419

    
420
        hql.append(" ORDER BY a.titleCache");
421
        return hql;
422
    }
423
    /**
424
     * @param taxonUuid
425
     * @param classificationUuid
426
     * @param agentUuid
427
     * @param relTypeUuid TODO
428
     * @param query
429
     * @param rankId TODO
430
     */
431
    private void setParamsForListTaxonNodeAgentRelations(UUID taxonUuid, UUID classificationUuid, UUID agentUuid,
432
            UUID rankUuid, UUID relTypeUuid, Query query) {
433

    
434
        if(taxonUuid != null) {
435
            query.setParameter("taxonUuid", taxonUuid);
436
        } else {
437
            if(rankUuid != null) {
438
                query.setParameter("rankUuid", rankUuid);
439
            }
440
        }
441
        if(classificationUuid != null) {
442
            query.setParameter("classificationUuid", classificationUuid);
443
        }
444
        if(agentUuid != null) {
445
            query.setParameter("agentUuid", agentUuid);
446
        }
447
        if(relTypeUuid != null) {
448
            query.setParameter("relTypeUuid", relTypeUuid);
449
        }
450
    }
451

    
452
    @Override
453
    public Map<TreeIndex, Integer> rankOrderIndexForTreeIndex(List<TreeIndex> treeIndexes,
454
            Integer minRankOrderIndex,
455
            Integer maxRankOrderIndex) {
456

    
457
        Map<TreeIndex, Integer> result = new HashMap<>();
458
        if (treeIndexes == null || treeIndexes.isEmpty()){
459
            return result;
460
        }
461

    
462
        String hql = " SELECT tn.treeIndex, r.orderIndex "
463
                + " FROM TaxonNode tn "
464
                + "     JOIN tn.taxon t "
465
                + "     JOIN t.name n "
466
                + "      JOIN n.rank r "
467
                + " WHERE tn.treeIndex IN (:treeIndexes) ";
468
        if (minRankOrderIndex != null){
469
            hql += " AND r.orderIndex <= :minOrderIndex";
470
        }
471
        if (maxRankOrderIndex != null){
472
            hql += " AND r.orderIndex >= :maxOrderIndex";
473
        }
474

    
475
        Query query =  getSession().createQuery(hql);
476
        query.setParameterList("treeIndexes", TreeIndex.toString(treeIndexes));
477
        if (minRankOrderIndex != null){
478
            query.setParameter("minOrderIndex", minRankOrderIndex);
479
        }
480
        if (maxRankOrderIndex != null){
481
            query.setParameter("maxOrderIndex", maxRankOrderIndex);
482
        }
483

    
484
        @SuppressWarnings("unchecked")
485
        List<Object[]> list = query.list();
486
        for (Object[] o : list){
487
            result.put(TreeIndex.NewInstance((String)o[0]), (Integer)o[1]);
488
        }
489
        return result;
490
    }
491

    
492
    @Override
493
    public Map<TreeIndex, UuidAndTitleCache<?>> taxonUuidsForTreeIndexes(Collection<TreeIndex> treeIndexes) {
494
        Map<TreeIndex, UuidAndTitleCache<?>> result = new HashMap<>();
495
        if (treeIndexes == null || treeIndexes.isEmpty()){
496
            return result;
497
        }
498

    
499
        String hql =
500
                  " SELECT tn.treeIndex, t.uuid, tnb.titleCache "
501
                + " FROM TaxonNode tn JOIN tn.taxon t Join t.name tnb "
502
                + " WHERE tn.treeIndex IN (:treeIndexes) ";
503
        Query query =  getSession().createQuery(hql);
504
        query.setParameterList("treeIndexes", TreeIndex.toString(treeIndexes));
505

    
506
        @SuppressWarnings("unchecked")
507
        List<Object[]> list = query.list();
508
        for (Object[] o : list){
509
            result.put(TreeIndex.NewInstance((String)o[0]), new UuidAndTitleCache<>((UUID)o[1], null, (String)o[2]));
510
        }
511
        return result;
512
    }
513

    
514
    /**
515
     * {@inheritDoc}
516
     */
517
    @Override
518
    public int countSecundumForSubtreeAcceptedTaxa(TreeIndex subTreeIndex, Reference newSec,
519
            boolean overwriteExisting, boolean includeSharedTaxa, boolean emptySecundumDetail) {
520
        String queryStr = acceptedForSubtreeQueryStr(includeSharedTaxa, subTreeIndex, SelectMode.COUNT);
521
        if (!overwriteExisting){
522
            queryStr += " AND t.sec IS NULL ";
523
        }
524
        return countResult(queryStr);
525
    }
526
    /**
527
     * @param queryStr
528
     * @return
529
     */
530
    private int countResult(String queryStr) {
531
        Query query = getSession().createQuery(queryStr);
532
        return ((Long)query.uniqueResult()).intValue();
533
    }
534
    /**
535
     * {@inheritDoc}
536
     */
537
    @Override
538
    public int countSecundumForSubtreeSynonyms(TreeIndex subTreeIndex, Reference newSec,
539
            boolean overwriteExisting, boolean includeSharedTaxa, boolean emptySecundumDetail) {
540
        String queryStr = synonymForSubtreeQueryStr(includeSharedTaxa, subTreeIndex, SelectMode.COUNT);
541
        if (!overwriteExisting){
542
            queryStr += " AND syn.sec IS NULL ";
543
        }
544
        return countResult(queryStr);
545
    }
546

    
547

    
548
    /**
549
     * {@inheritDoc}
550
     */
551
    //#3465
552
    @Override
553
    public Set<TaxonBase> setSecundumForSubtreeAcceptedTaxa(TreeIndex subTreeIndex, Reference newSec,
554
            boolean overwriteExisting, boolean includeSharedTaxa, boolean emptyDetail, IProgressMonitor monitor) {
555
        //for some reason this does not work, maybe because the listeners are not activated,
556
        //but also the first taxon for some reason does not get updated in terms of secundum, but only by the update listener
557
//        String where = "SELECT t.id FROM TaxonNode tn JOIN tn.taxon t " +
558
//                " WHERE tn.treeIndex like '%s%%' ORDER BY t.id";
559
//        where = String.format(where, subTreeIndex.toString());
560
//        Query query1 = getSession().createQuery(where);
561
//        List l = query1.list();
562
//
563
//        String hql = "UPDATE Taxon SET sec = :newSec, publish=false WHERE id IN (" + where + ")";
564
//        Query query = getSession().createQuery(hql);
565
//        query.setParameter("newSec", newSec);
566
//        int n = query.executeUpdate();
567

    
568
        String queryStr = acceptedForSubtreeQueryStr(includeSharedTaxa, subTreeIndex, SelectMode.ID);
569
        if (!overwriteExisting){
570
            queryStr += " AND t.sec IS NULL ";
571
        }
572
        return setSecundum(newSec, emptyDetail, queryStr, monitor);
573

    
574
    }
575

    
576
    @Override
577
    public Set<TaxonBase> setSecundumForSubtreeSynonyms(TreeIndex subTreeIndex, Reference newSec,
578
            boolean overwriteExisting, boolean includeSharedTaxa, boolean emptyDetail, IProgressMonitor monitor) {
579

    
580
        String queryStr = synonymForSubtreeQueryStr(includeSharedTaxa, subTreeIndex, SelectMode.ID);
581
        if (!overwriteExisting){
582
            queryStr += " AND syn.sec IS NULL ";
583
        }
584
        return setSecundum(newSec, emptyDetail, queryStr, monitor);
585
    }
586
    /**
587
     * @param newSec
588
     * @param emptyDetail
589
     * @param queryStr
590
     * @param monitor
591
     * @return
592
     */
593
    @SuppressWarnings("unchecked")
594
    private <T extends TaxonBase<?>> Set<T> setSecundum(Reference newSec, boolean emptyDetail, String queryStr, IProgressMonitor monitor) {
595
        Set<T> result = new HashSet<>();
596
        Query query = getSession().createQuery(queryStr);
597
        List<List<Integer>> partitionList = splitIdList(query.list(), DEFAULT_PARTITION_SIZE);
598
        for (List<Integer> taxonIdList : partitionList){
599
            List<TaxonBase> taxonList = taxonDao.loadList(taxonIdList, null);
600
            for (TaxonBase taxonBase : taxonList){
601
                if (taxonBase != null){
602
                    taxonBase = taxonDao.load(taxonBase.getUuid());
603
                    taxonBase.setSec(newSec);
604
                    if (emptyDetail){
605
                        taxonBase.setSecMicroReference(null);
606
                    }
607
                    result.add((T)CdmBase.deproxy(taxonBase));
608
                    monitor.worked(1);
609
                    if (monitor.isCanceled()){
610
                        return result;
611
                    }
612
                }
613
            }
614
        }
615
        return result;
616
    }
617

    
618
    private List<List<Integer>> splitIdList(List<Integer> idList, Integer size){
619
        List<List<Integer>> result = new ArrayList<>();
620
        for (int i = 0; (i*size)<idList.size(); i++) {
621
            int upper = Math.min((i+1)*size, idList.size());
622
            result.add(idList.subList(i*size, upper));
623
        }
624
        return result;
625
    }
626

    
627
    /**
628
     * {@inheritDoc}
629
     */
630
    @Override
631
    public int countPublishForSubtreeAcceptedTaxa(TreeIndex subTreeIndex, boolean publish, boolean includeSharedTaxa) {
632
        String queryStr = acceptedForSubtreeQueryStr(includeSharedTaxa, subTreeIndex, SelectMode.COUNT);
633
        queryStr += " AND t.publish != :publish ";
634
        Query query = getSession().createQuery(queryStr);
635
        query.setBoolean("publish", publish);
636
        return ((Long)query.uniqueResult()).intValue();
637
//        return countResult(queryStr);
638
    }
639
    /**
640
     * {@inheritDoc}
641
     */
642
    @Override
643
    public int countPublishForSubtreeSynonyms(TreeIndex subTreeIndex, boolean publish, boolean includeSharedTaxa) {
644
        String queryStr = synonymForSubtreeQueryStr(includeSharedTaxa, subTreeIndex, SelectMode.COUNT);
645
        queryStr += " AND syn.publish != :publish ";
646
        Query query = getSession().createQuery(queryStr);
647
        query.setBoolean("publish", publish);
648
        return ((Long)query.uniqueResult()).intValue();
649
//      return countResult(queryStr);
650
    }
651
    /**
652
     * {@inheritDoc}
653
     */
654
    @Override
655
    public Set<TaxonBase> setPublishForSubtreeAcceptedTaxa(TreeIndex subTreeIndex, boolean publish,
656
            boolean includeSharedTaxa, IProgressMonitor monitor) {
657
        String queryStr = acceptedForSubtreeQueryStr(includeSharedTaxa, subTreeIndex, SelectMode.ID);
658
        queryStr += " AND t.publish != :publish ";
659
        return setPublish(publish, queryStr, monitor);
660
    }
661

    
662
    /**
663
     * {@inheritDoc}
664
     */
665
    @Override
666
    public Set<TaxonBase> setPublishForSubtreeSynonyms(TreeIndex subTreeIndex, boolean publish,
667
            boolean includeSharedTaxa, IProgressMonitor monitor) {
668
        String queryStr = synonymForSubtreeQueryStr(includeSharedTaxa, subTreeIndex, SelectMode.ID);
669
        queryStr += " AND syn.publish != :publish ";
670
        return setPublish(publish, queryStr, monitor);
671
    }
672

    
673
    private static final int DEFAULT_PARTITION_SIZE = 100;
674
    /**
675
     * @param publish
676
     * @param queryStr
677
     * @return
678
     */
679
    private <T extends TaxonBase<?>> Set<T> setPublish(boolean publish, String queryStr, IProgressMonitor monitor) {
680
        Set<T> result = new HashSet<>();
681
        Query query = getSession().createQuery(queryStr);
682
        query.setBoolean("publish", publish);
683
        @SuppressWarnings("unchecked")
684
        List<List<Integer>> partitionList = splitIdList(query.list(), DEFAULT_PARTITION_SIZE);
685
        for (List<Integer> taxonIdList : partitionList){
686
            List<TaxonBase> taxonList = taxonDao.loadList(taxonIdList, null);
687
            for (TaxonBase taxonBase : taxonList){
688
                if (taxonBase != null){
689
                    taxonBase.setPublish(publish);
690
                    result.add((T)CdmBase.deproxy(taxonBase));
691
                    monitor.worked(1);
692
                    if (monitor.isCanceled()){
693
                        return result;
694
                    }
695
                }
696
            }
697
        }
698
        return result;
699
    }
700

    
701

    
702
    /**
703
     * @param includeSharedTaxa
704
     * @param subTreeIndex
705
     * @return
706
     */
707
    private String synonymForSubtreeQueryStr(boolean includeSharedTaxa, TreeIndex subTreeIndex, SelectMode mode) {
708
        String queryStr = "SELECT " + mode.hql("syn")
709
                + " FROM TaxonNode tn "
710
                + "   JOIN tn.taxon t "
711
                + "   JOIN t.synonyms syn"
712
                + " WHERE tn.treeIndex LIKE '%s%%' ";
713
        if (!includeSharedTaxa){
714
            queryStr += " AND t.taxonNodes.size <= 1  ";
715
        }
716
        queryStr = String.format(queryStr, subTreeIndex.toString());
717

    
718
        return queryStr;
719
    }
720

    
721
    private enum SelectMode{
722
        COUNT(" count(*) "),
723
        ID ("id "),
724
        UUID("uuid "),
725
        FULL("");
726
        private String hql;
727
        SelectMode(String hql){
728
            this.hql = hql;
729
        }
730
        public String hql(String prefix){
731
            switch (this){
732
            case ID:
733
            case UUID:
734
                return CdmUtils.Nz(prefix)+"." + hql;
735
            case FULL:
736
                return CdmUtils.Nz(prefix) + hql;
737
            case COUNT:
738
            default: return hql;
739
            }
740

    
741
        }
742
    }
743

    
744
    private String acceptedForSubtreeQueryStr(boolean includeSharedTaxa, TreeIndex subTreeIndex, SelectMode mode) {
745
        String queryStr = "SELECT " + mode.hql("t")
746
                + " FROM TaxonNode tn JOIN tn.taxon t "
747
                + " WHERE tn.treeIndex like '%s%%' ";
748
        if (!includeSharedTaxa){
749
            queryStr += " AND t.taxonNodes.size <= 1  ";
750
        }
751
        queryStr = String.format(queryStr, subTreeIndex.toString());
752

    
753
        return queryStr;
754
    }
755

    
756
    @Override
757
    public List<UuidAndTitleCache<TaxonNode>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification, Integer limit, String pattern, boolean searchForClassifications) {
758
        int classificationId = classification.getId();
759

    
760
         String queryString =
761
                   " SELECT nodes.uuid, nodes.id,  taxon.titleCache, taxon.name.rank " +
762
                   " FROM TaxonNode AS nodes "
763
                   + "    JOIN nodes.taxon as taxon " +
764
                   " WHERE nodes.classification.id = " + classificationId ;
765
         if (pattern != null){
766
             if (pattern.equals("?")){
767
                 limit = null;
768
             } else{
769
                 if (!pattern.endsWith("*")){
770
                     pattern += "%";
771
                 }
772
                 pattern = pattern.replace("*", "%");
773
                 pattern = pattern.replace("?", "%");
774
                 queryString = queryString + " AND taxon.titleCache LIKE (:pattern) " ;
775
             }
776
         }
777

    
778
         Query query = getSession().createQuery(queryString);
779

    
780
         if (limit != null){
781
             query.setMaxResults(limit);
782
         }
783

    
784
         if (pattern != null && !pattern.equals("?")){
785
             query.setParameter("pattern", pattern);
786
         }
787
         @SuppressWarnings("unchecked")
788
         List<Object[]> result = query.list();
789

    
790
         if (searchForClassifications){
791
             queryString = ""
792
                     + " SELECT nodes.uuid, nodes.id,  nodes.classification.titleCache, NULLIF(1,1) "
793
                     + " FROM TaxonNode AS nodes "
794
                     + " WHERE nodes.classification.id = " + classificationId +
795
                          " AND nodes.taxon IS NULL";
796
             if (pattern != null){
797
                 if (pattern.equals("?")){
798
                     limit = null;
799
                 } else{
800
                     if (!pattern.endsWith("*")){
801
                         pattern += "%";
802
                     }
803
                     pattern = pattern.replace("*", "%");
804
                     pattern = pattern.replace("?", "%");
805
                     queryString = queryString + " AND nodes.classification.titleCache LIKE (:pattern) " ;
806
                 }
807
             }
808
             query = getSession().createQuery(queryString);
809

    
810
             if (limit != null){
811
                 query.setMaxResults(limit);
812
             }
813

    
814
             if (pattern != null && !pattern.equals("?")){
815
                 query.setParameter("pattern", pattern);
816
             }
817
             @SuppressWarnings("unchecked")
818
             List<Object[]> resultClassifications = query.list();
819

    
820
             result.addAll(resultClassifications);
821
         }
822

    
823
         if(result.size() == 0){
824
             return null;
825
         }else{
826
             List<UuidAndTitleCache<TaxonNode>> list = new ArrayList<>(result.size());
827
             if (result.iterator().next().length == 4){
828
                 Collections.sort(result, new UuidAndTitleCacheTaxonComparator());
829
             }
830
             for (Object object : result){
831
                 Object[] objectArray = (Object[]) object;
832
                 UUID uuid = (UUID)objectArray[0];
833
                 Integer id = (Integer) objectArray[1];
834
                 String titleCache = (String) objectArray[2];
835

    
836
                 list.add(new UuidAndTitleCache<>(TaxonNode.class, uuid, id, titleCache));
837
             }
838

    
839
             return list;
840
         }
841
    }
842

    
843
}
(4-4/5)