2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.taxon
;
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
;
22 import java
.util
.UUID
;
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
;
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
.name
.Rank
;
40 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
41 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
42 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
43 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
44 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
45 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
46 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
47 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNodeAgentRelation
;
48 import eu
.etaxonomy
.cdm
.model
.taxon
.UuidAndTitleCacheTaxonComparator
;
49 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.Restriction
;
50 import eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.common
.AnnotatableDaoImpl
;
51 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.IClassificationDao
;
52 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
53 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeDao
;
54 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNodeDto
;
55 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
56 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
63 @Qualifier("taxonNodeDaoHibernateImpl")
64 public class TaxonNodeDaoHibernateImpl
extends AnnotatableDaoImpl
<TaxonNode
>
65 implements ITaxonNodeDao
{
66 @SuppressWarnings("unused")
67 private static final Logger logger
= Logger
.getLogger(TaxonNodeDaoHibernateImpl
.class);
70 private ITaxonDao taxonDao
;
72 private IClassificationDao classificationDao
;
74 public TaxonNodeDaoHibernateImpl() {
75 super(TaxonNode
.class);
78 public UUID
delete(TaxonNode persistentObject
, boolean deleteChildren
){
79 Taxon taxon
= persistentObject
.getTaxon();
80 taxon
= HibernateProxyHelper
.deproxy(taxon
);
82 /*Session session = this.getSession();
83 Query query = session.createQuery("from TaxonNode t where t.taxon = :taxon");
84 query.setParameter("taxon", taxon);
85 List result = query.list();*/
87 Hibernate
.initialize(taxon
);
88 Hibernate
.initialize(taxon
.getTaxonNodes());
89 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
90 //Hibernate.initialize(taxon.getTaxonNodes());
91 for (TaxonNode node
:nodes
) {
92 node
= HibernateProxyHelper
.deproxy(node
);
94 if (node
.equals(persistentObject
)){
95 if (node
.hasChildNodes()){
96 Iterator
<TaxonNode
> childNodes
= node
.getChildNodes().iterator();
98 List
<TaxonNode
> listForDeletion
= new ArrayList
<>();
99 while (childNodes
.hasNext()){
100 childNode
= childNodes
.next();
101 listForDeletion
.add(childNode
);
105 for (TaxonNode deleteNode
:listForDeletion
){
106 delete(deleteNode
, deleteChildren
);
110 taxon
.removeTaxonNode(node
, deleteChildren
);
111 taxonDao
.saveOrUpdate(taxon
);
112 taxon
= HibernateProxyHelper
.deproxy(taxonDao
.findByUuid(taxon
.getUuid()), Taxon
.class);
113 taxonDao
.delete(taxon
);
119 UUID result
= super.delete(persistentObject
);
125 public List
<TaxonNode
> getTaxonOfAcceptedTaxaByClassification(Classification classification
, Integer start
, Integer end
) {
126 int classificationId
= classification
.getId();
128 if(start
!=null && end
!= null){
129 limit
= "LIMIT "+start
+"," +end
;
132 String queryString
= "SELECT DISTINCT nodes.*,taxa.titleCache "
133 + " FROM TaxonNode AS nodes "
134 + " LEFT JOIN TaxonBase AS taxa ON nodes.taxon_id = taxa.id "
135 + " WHERE taxa.DTYPE = 'Taxon' "
136 + " AND nodes.classification_id = " + classificationId
+
137 " ORDER BY taxa.titleCache " + limit
;
138 @SuppressWarnings("unchecked")
139 List
<TaxonNode
> result
= getSession().createSQLQuery(queryString
).addEntity(TaxonNode
.class).list();
145 public int countTaxonOfAcceptedTaxaByClassification(Classification classification
){
146 int classificationId
= classification
.getId();
148 String queryString
= ""
149 + " SELECT DISTINCT COUNT('nodes.*') "
150 + " FROM TaxonNode AS nodes "
151 + " LEFT JOIN TaxonBase AS taxa ON nodes.taxon_id = taxa.id "
152 + " WHERE taxa.DTYPE = 'Taxon' AND nodes.classification_id = " + classificationId
;
153 @SuppressWarnings("unchecked")
154 List
<BigInteger
> result
= getSession().createSQLQuery(queryString
).list();
155 return result
.get(0).intValue ();
159 // public List<TaxonNodeDto> listChildNodesAsTaxonNodeDto(TaxonNodeDto parent) {
160 // String queryString =
161 // " SELECT tn.uuid, tn.id, t.titleCache "
162 // + " FROM TaxonNode tn "
163 // + " INNER JOIN tn.taxon AS t "
164 // + " WHERE tn.parent.uuid = :parentId";
166 // Query query = getSession().createQuery(queryString);
167 // query.setParameter("parentId", parent.getUuid());
169 // @SuppressWarnings("unchecked")
170 // List<Object[]> result = query.list();
172 // List<TaxonNodeDto> list = new ArrayList<>();
173 // for(Object[] object : result){
174 // list.add(new TaxonNodeDto((UUID) object[0],(Integer) object[1], (String) object[2]));
180 public List
<TaxonNodeDto
> listChildNodesAsTaxonNodeDto(TaxonNodeDto parent
) {
183 + " FROM TaxonNode tn "
184 + " INNER JOIN tn.taxon AS t "
185 + " WHERE tn.parent.uuid = :parentId";
186 Query query
= getSession().createQuery(queryString
);
187 query
.setParameter("parentId", parent
.getUuid());
189 @SuppressWarnings("unchecked")
190 List
<TaxonNode
> result
= query
.list();
192 List
<TaxonNodeDto
> list
= new ArrayList
<>();
193 for(TaxonNode object
: result
){
194 list
.add(new TaxonNodeDto(object
));
200 public List
<UuidAndTitleCache
<TaxonNode
>> getUuidAndTitleCache(Integer limit
, String pattern
, UUID classificationUuid
) {
201 String queryString
= "SELECT tn.uuid, tn.id, t.titleCache, t.name.rank "
202 + " FROM TaxonNode tn "
203 + " INNER JOIN tn.taxon AS t "
204 + " INNER JOIN tn.classification AS cls "
205 + "WHERE t.titleCache LIKE :pattern ";
206 if(classificationUuid
!= null){
207 queryString
+= "AND cls.uuid = :classificationUuid";
209 Query query
= getSession().createQuery(queryString
);
211 query
.setParameter("pattern", pattern
.toLowerCase()+"%");
212 query
.setParameter("classificationUuid", classificationUuid
);
215 @SuppressWarnings("unchecked")
216 List
<Object
[]> result
= query
.list();
217 Collections
.sort(result
, new UuidAndTitleCacheTaxonComparator());
219 List
<UuidAndTitleCache
<TaxonNode
>> list
= new ArrayList
<>();
220 for(Object
[] object
: result
){
221 list
.add(new UuidAndTitleCache
<TaxonNode
>((UUID
) object
[0],(Integer
) object
[1], (String
) object
[2]));
230 public TaxonNodeDto
getParentUuidAndTitleCache(TaxonNodeDto child
) {
231 String queryString
= ""
232 + " SELECT tn.parent.uuid, tn.parent.id, tn.parent.taxon.titleCache, "
233 + " tn.parent.classification.titleCache "
234 + " FROM TaxonNode tn"
235 + " LEFT OUTER JOIN tn.parent.taxon"
236 + " WHERE tn.id = :childId";
237 Query query
= getSession().createQuery(queryString
);
238 query
.setParameter("childId", child
.getId());
239 List
<TaxonNodeDto
> list
= new ArrayList
<>();
241 @SuppressWarnings("unchecked")
242 List
<Object
[]> result
= query
.list();
244 for(Object
[] object
: result
){
245 UUID uuid
= (UUID
) object
[0];
246 Integer id
= (Integer
) object
[1];
247 String taxonTitleCache
= (String
) object
[2];
248 String classificationTitleCache
= (String
) object
[3];
249 if(taxonTitleCache
!=null){
250 list
.add(new TaxonNodeDto(uuid
,id
, taxonTitleCache
));
253 list
.add(new TaxonNodeDto(uuid
,id
, classificationTitleCache
));
257 return list
.iterator().next();
263 public List
<TaxonNode
> listChildrenOf(TaxonNode node
, Integer pageSize
, Integer pageIndex
,
264 boolean recursive
, boolean includeUnpublished
, List
<String
> propertyPaths
){
266 if (recursive
== true){
267 Criteria crit
= childrenOfCriteria(node
, includeUnpublished
);
269 if(pageSize
!= null) {
270 crit
.setMaxResults(pageSize
);
271 if(pageIndex
!= null) {
272 crit
.setFirstResult(pageIndex
* pageSize
);
274 crit
.setFirstResult(0);
277 @SuppressWarnings("unchecked")
278 List
<TaxonNode
> results
= crit
.list();
279 results
.remove(node
);
280 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
283 return classificationDao
.listChildrenOf(node
.getTaxon(), node
.getClassification(), null,
284 includeUnpublished
, pageSize
, pageIndex
, propertyPaths
);
290 public Long
countChildrenOf(TaxonNode node
, Classification classification
,
291 boolean recursive
, boolean includeUnpublished
) {
293 if (recursive
== true){
294 Criteria crit
= childrenOfCriteria(node
, includeUnpublished
);
295 crit
.setProjection(Projections
.rowCount());
296 return ((Integer
)crit
.uniqueResult().hashCode()).longValue();
298 return classificationDao
.countChildrenOf(
299 node
.getTaxon(), classification
, null, includeUnpublished
);
304 * @param includeUnpublished
307 private Criteria
childrenOfCriteria(TaxonNode node
, boolean includeUnpublished
) {
308 Criteria crit
= getSession().createCriteria(TaxonNode
.class);
309 crit
.add( Restrictions
.like("treeIndex", node
.treeIndex()+ "%") );
310 if (!includeUnpublished
){
311 crit
.createCriteria("taxon").add( Restrictions
.eq("publish", Boolean
.TRUE
));
319 public List
<TaxonNodeAgentRelation
> listTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
,
320 UUID agentUuid
, UUID rankUuid
, UUID relTypeUuid
, Integer start
, Integer limit
,
321 List
<String
> propertyPaths
) {
323 StringBuilder hql
= prepareListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
,
324 agentUuid
, rankUuid
, relTypeUuid
, false);
326 Query query
= getSession().createQuery(hql
.toString());
329 query
.setMaxResults(limit
);
331 query
.setFirstResult(start
);
335 setParamsForListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
, agentUuid
, rankUuid
, relTypeUuid
, query
);
337 @SuppressWarnings("unchecked")
338 List
<TaxonNodeAgentRelation
> records
= query
.list();
340 if(propertyPaths
!= null) {
341 defaultBeanInitializer
.initializeAll(records
, propertyPaths
);
347 public <S
extends TaxonNode
> List
<S
> list(Class
<S
> type
, List
<Restriction
<?
>> restrictions
, Integer limit
,
348 Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
349 // TODO Auto-generated method stub
350 return list(type
, restrictions
, limit
, start
, orderHints
, propertyPaths
, INCLUDE_UNPUBLISHED
);
354 public <S
extends TaxonNode
> List
<S
> list(Class
<S
> type
, List
<Restriction
<?
>> restrictions
, Integer limit
,
355 Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, boolean includePublished
) {
357 Criteria criteria
= createCriteria(type
, restrictions
, false);
359 if(!includePublished
){
360 criteria
.add(Restrictions
.eq("taxon.publish", true));
363 addLimitAndStart(criteria
, limit
, start
);
364 addOrder(criteria
, orderHints
);
366 @SuppressWarnings("unchecked")
367 List
<S
> result
= criteria
.list();
368 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
374 public long count(Class
<?
extends TaxonNode
> type
, List
<Restriction
<?
>> restrictions
) {
375 return count(type
, restrictions
, INCLUDE_UNPUBLISHED
);
380 public long count(Class
<?
extends TaxonNode
> type
, List
<Restriction
<?
>> restrictions
, boolean includePublished
) {
382 Criteria criteria
= createCriteria(type
, restrictions
, false);
383 if(!includePublished
){
384 criteria
.add(Restrictions
.eq("taxon.publish", true));
386 criteria
.setProjection(Projections
.projectionList().add(Projections
.rowCount()));
387 return (Long
) criteria
.uniqueResult();
395 public long countTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
, UUID agentUuid
, UUID rankUuid
, UUID relTypeUuid
) {
397 StringBuilder hql
= prepareListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
, agentUuid
, rankUuid
, relTypeUuid
, true);
398 Query query
= getSession().createQuery(hql
.toString());
400 setParamsForListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
, agentUuid
, rankUuid
, relTypeUuid
, query
);
402 Long count
= Long
.parseLong(query
.uniqueResult().toString());
408 * @param classificationUuid
410 * @param relTypeUuid TODO
411 * @param doCount TODO
413 * limit to taxa having this rank, only applies if <code>taxonUuid = null</code>
416 private StringBuilder
prepareListTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
, UUID agentUuid
, UUID rankUuid
, UUID relTypeUuid
, boolean doCount
) {
418 StringBuilder hql
= new StringBuilder();
420 String join_fetch_mode
= doCount ?
"JOIN" : "JOIN FETCH";
423 hql
.append("SELECT COUNT(tnar)");
425 hql
.append("SELECT tnar");
428 hql
.append(" FROM TaxonNodeAgentRelation AS tnar ");
429 if(taxonUuid
!= null) {
430 // taxonUuid is search filter, do not fetch it
431 hql
.append(" JOIN tnar.taxonNode AS tn "
432 + " JOIN tn.taxon AS t ");
434 hql
.append(join_fetch_mode
)
435 .append(" tnar.taxonNode AS tn ")
436 .append(join_fetch_mode
).append(" tn.taxon AS t ");
437 if(rankUuid
!= null) {
438 hql
.append(" join t.name as n ");
441 hql
.append(" JOIN tn.classification AS c ");
442 if(agentUuid
!= null) {
443 // agentUuid is search filter, do not fetch it
444 // hql.append(" join tnar.agent as a ");
445 hql
.append(join_fetch_mode
).append(" tnar.agent AS a ");
447 hql
.append(join_fetch_mode
).append(" tnar.agent AS a ");
450 hql
.append(" WHERE (1 = 1) ");
452 if(relTypeUuid
!= null) {
453 hql
.append(" AND tnar.type.uuid = :relTypeUuid ");
456 if(taxonUuid
!= null) {
457 hql
.append(" AND t.uuid = :taxonUuid ");
459 if(rankUuid
!= null) {
460 hql
.append(" AND n.rank.uuid = :rankUuid ");
463 if(classificationUuid
!= null) {
464 hql
.append(" AND c.uuid = :classificationUuid ");
466 if(agentUuid
!= null) {
467 hql
.append(" AND a.uuid = :agentUuid ");
470 hql
.append(" ORDER BY a.titleCache");
475 * @param classificationUuid
477 * @param relTypeUuid TODO
481 private void setParamsForListTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
, UUID agentUuid
,
482 UUID rankUuid
, UUID relTypeUuid
, Query query
) {
484 if(taxonUuid
!= null) {
485 query
.setParameter("taxonUuid", taxonUuid
);
487 if(rankUuid
!= null) {
488 query
.setParameter("rankUuid", rankUuid
);
491 if(classificationUuid
!= null) {
492 query
.setParameter("classificationUuid", classificationUuid
);
494 if(agentUuid
!= null) {
495 query
.setParameter("agentUuid", agentUuid
);
497 if(relTypeUuid
!= null) {
498 query
.setParameter("relTypeUuid", relTypeUuid
);
503 public Map
<TreeIndex
, Integer
> rankOrderIndexForTreeIndex(List
<TreeIndex
> treeIndexes
,
504 Integer minRankOrderIndex
,
505 Integer maxRankOrderIndex
) {
507 Map
<TreeIndex
, Integer
> result
= new HashMap
<>();
508 if (treeIndexes
== null || treeIndexes
.isEmpty()){
512 String hql
= " SELECT tn.treeIndex, r.orderIndex "
513 + " FROM TaxonNode tn "
514 + " JOIN tn.taxon t "
517 + " WHERE tn.treeIndex IN (:treeIndexes) ";
518 if (minRankOrderIndex
!= null){
519 hql
+= " AND r.orderIndex <= :minOrderIndex";
521 if (maxRankOrderIndex
!= null){
522 hql
+= " AND r.orderIndex >= :maxOrderIndex";
525 Query query
= getSession().createQuery(hql
);
526 query
.setParameterList("treeIndexes", TreeIndex
.toString(treeIndexes
));
527 if (minRankOrderIndex
!= null){
528 query
.setParameter("minOrderIndex", minRankOrderIndex
);
530 if (maxRankOrderIndex
!= null){
531 query
.setParameter("maxOrderIndex", maxRankOrderIndex
);
534 @SuppressWarnings("unchecked")
535 List
<Object
[]> list
= query
.list();
536 for (Object
[] o
: list
){
537 result
.put(TreeIndex
.NewInstance((String
)o
[0]), (Integer
)o
[1]);
543 public Map
<TreeIndex
, UuidAndTitleCache
<?
>> taxonUuidsForTreeIndexes(Collection
<TreeIndex
> treeIndexes
) {
544 Map
<TreeIndex
, UuidAndTitleCache
<?
>> result
= new HashMap
<>();
545 if (treeIndexes
== null || treeIndexes
.isEmpty()){
550 " SELECT tn.treeIndex, t.uuid, tnb.titleCache "
551 + " FROM TaxonNode tn JOIN tn.taxon t Join t.name tnb "
552 + " WHERE tn.treeIndex IN (:treeIndexes) ";
553 Query query
= getSession().createQuery(hql
);
554 query
.setParameterList("treeIndexes", TreeIndex
.toString(treeIndexes
));
556 @SuppressWarnings("unchecked")
557 List
<Object
[]> list
= query
.list();
558 for (Object
[] o
: list
){
559 result
.put(TreeIndex
.NewInstance((String
)o
[0]), new UuidAndTitleCache
<>((UUID
)o
[1], null, (String
)o
[2]));
565 public List
<TaxonNodeDto
> getParentTaxonNodeDtoForRank(
566 Classification classification
, Rank rank
, TaxonBase
<?
> taxonBase
) {
569 if (taxonBase
instanceof Taxon
) {
570 taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
572 taxon
= CdmBase
.deproxy(((Synonym
)taxonBase
).getAcceptedTaxon());
574 TaxonNode node
= null;
576 node
= taxon
.getTaxonNode(classification
);
578 List
<TaxonNodeDto
> result
= new ArrayList
<>();
580 String treeIndex
= node
.treeIndex();
581 List
<Integer
> ancestorNodeIds
= TreeIndex
.NewInstance(treeIndex
).parentNodeIds(false);
583 Criteria nodeCrit
= getSession().createCriteria(TaxonNode
.class);
584 Criteria taxonCrit
= nodeCrit
.createCriteria("taxon");
585 Criteria nameCrit
= taxonCrit
.createCriteria("name");
586 nodeCrit
.add(Restrictions
.in("id", ancestorNodeIds
));
587 nodeCrit
.add(Restrictions
.eq("classification", classification
));
588 nameCrit
.add(Restrictions
.eq("rank", rank
));
590 @SuppressWarnings("unchecked")
591 List
<TaxonNode
> list
= nodeCrit
.list();
592 for (TaxonNode rankNode
: list
){
593 TaxonNodeDto dto
= new TaxonNodeDto(rankNode
);
602 public List
<TaxonNodeDto
> getParentTaxonNodeDtoForRank(
603 Classification classification
, Rank rank
, TaxonName name
) {
605 Set
<TaxonBase
> taxa
= name
.getTaxonBases();
606 List
<TaxonNodeDto
> result
= new ArrayList
<>();
607 for (TaxonBase
<?
> taxonBase
:taxa
) {
608 List
<TaxonNodeDto
> tmpList
= getParentTaxonNodeDtoForRank(classification
, rank
, taxonBase
);
609 for (TaxonNodeDto tmpDto
: tmpList
){
610 boolean exists
= false; //an equal method does not yet exist for TaxonNodeDto therefore this workaround
611 for (TaxonNodeDto dto
: result
){
612 if (dto
.getTreeIndex().equals(tmpDto
.getTreeIndex())){
625 public int countSecundumForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, Reference newSec
,
626 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptySecundumDetail
) {
627 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.COUNT
);
628 if (!overwriteExisting
){
629 queryStr
+= " AND t.sec IS NULL ";
631 return countResult(queryStr
);
634 private int countResult(String queryStr
) {
635 Query query
= getSession().createQuery(queryStr
);
636 return ((Long
)query
.uniqueResult()).intValue();
640 public int countSecundumForSubtreeSynonyms(TreeIndex subTreeIndex
, Reference newSec
,
641 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptySecundumDetail
) {
642 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.COUNT
);
643 if (!overwriteExisting
){
644 queryStr
+= " AND syn.sec IS NULL ";
646 return countResult(queryStr
);
651 public Set
<TaxonBase
> setSecundumForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, Reference newSec
,
652 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptyDetail
, IProgressMonitor monitor
) {
653 //for some reason this does not work, maybe because the listeners are not activated,
654 //but also the first taxon for some reason does not get updated in terms of secundum, but only by the update listener
655 // String where = "SELECT t.id FROM TaxonNode tn JOIN tn.taxon t " +
656 // " WHERE tn.treeIndex like '%s%%' ORDER BY t.id";
657 // where = String.format(where, subTreeIndex.toString());
658 // Query query1 = getSession().createQuery(where);
659 // List l = query1.list();
661 // String hql = "UPDATE Taxon SET sec = :newSec, publish=false WHERE id IN (" + where + ")";
662 // Query query = getSession().createQuery(hql);
663 // query.setParameter("newSec", newSec);
664 // int n = query.executeUpdate();
666 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.ID
);
667 if (!overwriteExisting
){
668 queryStr
+= " AND t.sec IS NULL ";
670 return setSecundum(newSec
, emptyDetail
, queryStr
, monitor
);
675 public Set
<TaxonBase
> setSecundumForSubtreeSynonyms(TreeIndex subTreeIndex
, Reference newSec
,
676 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptyDetail
, IProgressMonitor monitor
) {
678 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.ID
);
679 if (!overwriteExisting
){
680 queryStr
+= " AND syn.sec IS NULL ";
682 return setSecundum(newSec
, emptyDetail
, queryStr
, monitor
);
685 @SuppressWarnings("unchecked")
686 private <T
extends TaxonBase
<?
>> Set
<T
> setSecundum(Reference newSec
, boolean emptyDetail
, String queryStr
, IProgressMonitor monitor
) {
687 Set
<T
> result
= new HashSet
<>();
688 Query query
= getSession().createQuery(queryStr
);
689 List
<List
<Integer
>> partitionList
= splitIdList(query
.list(), DEFAULT_PARTITION_SIZE
);
690 for (List
<Integer
> taxonIdList
: partitionList
){
691 List
<TaxonBase
> taxonList
= taxonDao
.loadList(taxonIdList
, null, null);
692 for (TaxonBase taxonBase
: taxonList
){
693 if (taxonBase
!= null){
694 taxonBase
= taxonDao
.load(taxonBase
.getUuid());
695 taxonBase
.setSec(newSec
);
697 taxonBase
.setSecMicroReference(null);
699 result
.add((T
)CdmBase
.deproxy(taxonBase
));
701 if (monitor
.isCanceled()){
710 private List
<List
<Integer
>> splitIdList(List
<Integer
> idList
, Integer size
){
711 List
<List
<Integer
>> result
= new ArrayList
<>();
712 for (int i
= 0; (i
*size
)<idList
.size(); i
++) {
713 int upper
= Math
.min((i
+1)*size
, idList
.size());
714 result
.add(idList
.subList(i
*size
, upper
));
720 public int countPublishForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, boolean publish
, boolean includeSharedTaxa
, boolean includeHybrids
) {
721 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.COUNT
);
722 queryStr
+= " AND t.publish != :publish ";
723 System
.out
.println(queryStr
);
724 Query query
= getSession().createQuery(queryStr
);
725 query
.setBoolean("publish", publish
);
726 return ((Long
)query
.uniqueResult()).intValue();
730 public int countPublishForSubtreeSynonyms(TreeIndex subTreeIndex
, boolean publish
, boolean includeSharedTaxa
, boolean includeHybrids
) {
731 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.COUNT
);
732 queryStr
+= " AND syn.publish != :publish ";
733 Query query
= getSession().createQuery(queryStr
);
734 query
.setBoolean("publish", publish
);
735 return ((Long
)query
.uniqueResult()).intValue();
739 public Set
<TaxonBase
> setPublishForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, boolean publish
,
740 boolean includeSharedTaxa
, boolean includeHybrids
, IProgressMonitor monitor
) {
741 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.ID
);
742 queryStr
+= " AND t.publish != :publish ";
743 return setPublish(publish
, queryStr
, null, monitor
);
747 public Set
<TaxonBase
> setPublishForSubtreeSynonyms(TreeIndex subTreeIndex
, boolean publish
,
748 boolean includeSharedTaxa
, boolean includeHybrids
, IProgressMonitor monitor
) {
749 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.ID
);
750 queryStr
+= " AND syn.publish != :publish ";
751 return setPublish(publish
, queryStr
, null, monitor
);
755 public int countPublishForSubtreeRelatedTaxa(TreeIndex subTreeIndex
, boolean publish
, boolean includeSharedTaxa
, boolean includeHybrids
) {
756 String queryStr
= forSubtreeRelatedTaxaQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.COUNT
);
757 queryStr
+= " AND relTax.publish != :publish ";
758 Query query
= getSession().createQuery(queryStr
);
759 query
.setBoolean("publish", publish
);
760 return ((Long
)query
.uniqueResult()).intValue();
764 public Set
<TaxonBase
> setPublishForSubtreeRelatedTaxa(TreeIndex subTreeIndex
, boolean publish
,
765 Set
<UUID
> relationTypes
, boolean includeSharedTaxa
, boolean includeHybrids
,
766 IProgressMonitor monitor
) {
767 String queryStr
= forSubtreeRelatedTaxaQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.ID
);
768 queryStr
+= " AND relTax.publish != :publish ";
769 queryStr
+= " AND rel.type.uuid IN (:relTypeUuid)";
770 return setPublish(publish
, queryStr
, relationTypes
, monitor
);
773 private static final int DEFAULT_PARTITION_SIZE
= 100;
775 private <T
extends TaxonBase
<?
>> Set
<T
> setPublish(boolean publish
, String queryStr
, Set
<UUID
> relTypeUuids
, IProgressMonitor monitor
) {
776 Set
<T
> result
= new HashSet
<>();
777 Query query
= getSession().createQuery(queryStr
);
778 query
.setBoolean("publish", publish
);
779 if (relTypeUuids
!= null && !relTypeUuids
.isEmpty()){
780 query
.setParameterList("relTypeUuid", relTypeUuids
);
782 @SuppressWarnings("unchecked")
783 List
<List
<Integer
>> partitionList
= splitIdList(query
.list(), DEFAULT_PARTITION_SIZE
);
784 for (List
<Integer
> taxonIdList
: partitionList
){
785 List
<TaxonBase
> taxonList
= taxonDao
.loadList(taxonIdList
, null, null);
786 for (TaxonBase
<?
> taxonBase
: taxonList
){
787 if (taxonBase
!= null){
788 taxonBase
.setPublish(publish
);
789 result
.add((T
)CdmBase
.deproxy(taxonBase
));
791 if (monitor
.isCanceled()){
800 private String
forSubtreeSynonymQueryStr(boolean includeSharedTaxa
, TreeIndex subTreeIndex
, boolean excludeHybrids
, SelectMode mode
) {
801 String queryStr
= "SELECT " + mode
.hql("syn")
802 + " FROM TaxonNode tn "
803 + " JOIN tn.taxon t "
804 + " JOIN t.synonyms syn LEFT JOIN syn.name n ";
805 String whereStr
= " tn.treeIndex LIKE '%1$s%%' ";
806 if (!includeSharedTaxa
){
807 whereStr
+= " AND NOT EXISTS ("
808 + "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
810 whereStr
= handleExcludeHybrids(whereStr
, excludeHybrids
, "syn");
811 queryStr
+= " WHERE " + String
.format(whereStr
, subTreeIndex
.toString());
816 private String
handleExcludeHybrids(String whereStr
, boolean excludeHybrids
, String t
) {
819 String hybridWhere
= " AND (n is NULL OR "
820 + " (n.monomHybrid=0 AND n.binomHybrid=0 "
821 + " AND n.trinomHybrid=0 AND n.hybridFormula=0 )) ";
823 whereStr
+= hybridWhere
; //String.format(hybridWhere, t);
828 private String
forSubtreeRelatedTaxaQueryStr(boolean includeSharedTaxa
, TreeIndex subTreeIndex
,
829 boolean excludeHybrids
, SelectMode mode
) {
830 String queryStr
= "SELECT " + mode
.hql("relTax")
831 + " FROM TaxonNode tn "
832 + " JOIN tn.taxon t "
833 + " JOIN t.relationsToThisTaxon rel"
834 + " JOIN rel.relatedFrom relTax LEFT JOIN relTax.name n ";
835 String whereStr
=" tn.treeIndex LIKE '%1$s%%' ";
836 if (!includeSharedTaxa
){
837 //toTaxon should only be used in the given subtree
838 whereStr
+= " AND NOT EXISTS ("
839 + "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
840 //from taxon should not be used in another classification
841 whereStr
+= " AND NOT EXISTS ("
842 + "FROM TaxonNode tn3 WHERE tn3.taxon = relTax AND tn3.treeIndex not like '%1$s%%') ";
843 //fromTaxon should not be related as e.g. pro parte synonym or misapplication to
844 //another taxon which is not part of the subtree
845 //TODO and has not the publish state
846 whereStr
+= " AND NOT EXISTS ("
847 + "FROM TaxonNode tn4 JOIN tn4.taxon t2 JOIN t2.relationsToThisTaxon rel2 "
848 + " WHERE rel2.relatedFrom = relTax AND tn4.treeIndex not like '%1$s%%' "
849 + " AND tn4.taxon.publish != :publish ) ";
851 whereStr
= handleExcludeHybrids(whereStr
, excludeHybrids
, "relTax");
852 queryStr
+= " WHERE " + String
.format(whereStr
, subTreeIndex
.toString());
857 private enum SelectMode
{
863 SelectMode(String hql
){
866 public String
hql(String prefix
){
870 return CdmUtils
.Nz(prefix
)+"." + hql
;
872 return CdmUtils
.Nz(prefix
) + hql
;
880 private String
forSubtreeAcceptedQueryStr(boolean includeSharedTaxa
, TreeIndex subTreeIndex
, boolean excludeHybrids
, SelectMode mode
) {
881 String queryStr
= "SELECT " + mode
.hql("t")
882 + " FROM TaxonNode tn JOIN tn.taxon t LEFT JOIN t.name n ";
883 String whereStr
= " tn.treeIndex like '%1$s%%' ";
884 if (!includeSharedTaxa
){
885 whereStr
+= " AND NOT EXISTS ("
886 + "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
888 whereStr
= handleExcludeHybrids(whereStr
, excludeHybrids
, "t");
889 queryStr
+= " WHERE " + String
.format(whereStr
, subTreeIndex
.toString());
895 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification
, Integer limit
, String pattern
, boolean searchForClassifications
) {
896 int classificationId
= classification
.getId();
899 " SELECT nodes.uuid, nodes.id, taxon.titleCache, taxon.name.rank " +
900 " FROM TaxonNode AS nodes "
901 + " JOIN nodes.taxon as taxon " +
902 " WHERE nodes.classification.id = " + classificationId
;
903 if (pattern
!= null){
904 if (pattern
.equals("?")){
907 if (!pattern
.endsWith("*")){
910 pattern
= pattern
.replace("*", "%");
911 pattern
= pattern
.replace("?", "%");
912 queryString
= queryString
+ " AND taxon.titleCache LIKE (:pattern) " ;
916 Query query
= getSession().createQuery(queryString
);
919 query
.setMaxResults(limit
);
922 if (pattern
!= null && !pattern
.equals("?")){
923 query
.setParameter("pattern", pattern
);
925 @SuppressWarnings("unchecked")
926 List
<Object
[]> result
= query
.list();
928 if (searchForClassifications
){
930 + " SELECT nodes.uuid, nodes.id, nodes.classification.titleCache, NULLIF(1,1) "
931 + " FROM TaxonNode AS nodes "
932 + " WHERE nodes.classification.id = " + classificationId
+
933 " AND nodes.taxon IS NULL";
934 if (pattern
!= null){
935 if (pattern
.equals("?")){
938 if (!pattern
.endsWith("*")){
941 pattern
= pattern
.replace("*", "%");
942 pattern
= pattern
.replace("?", "%");
943 queryString
= queryString
+ " AND nodes.classification.titleCache LIKE (:pattern) " ;
946 query
= getSession().createQuery(queryString
);
949 query
.setMaxResults(limit
);
952 if (pattern
!= null && !pattern
.equals("?")){
953 query
.setParameter("pattern", pattern
);
955 @SuppressWarnings("unchecked")
956 List
<Object
[]> resultClassifications
= query
.list();
958 result
.addAll(resultClassifications
);
961 if(result
.size() == 0){
964 List
<UuidAndTitleCache
<TaxonNode
>> list
= new ArrayList
<>(result
.size());
965 if (result
.iterator().next().length
== 4){
966 Collections
.sort(result
, new UuidAndTitleCacheTaxonComparator());
968 for (Object object
: result
){
969 Object
[] objectArray
= (Object
[]) object
;
970 UUID uuid
= (UUID
)objectArray
[0];
971 Integer id
= (Integer
) objectArray
[1];
972 String titleCache
= (String
) objectArray
[2];
974 list
.add(new UuidAndTitleCache
<>(TaxonNode
.class, uuid
, id
, titleCache
));
982 public List
<TaxonNodeDto
> getTaxonNodeDto(Integer limit
, String pattern
, UUID classificationUuid
) {
983 String queryString
= "SELECT tn.uuid, tn.id, t.titleCache, t.name.rank "
984 + " FROM TaxonNode tn "
985 + " INNER JOIN tn.taxon AS t "
986 + " INNER JOIN tn.classification AS cls "
987 + "WHERE t.titleCache LIKE :pattern ";
988 if(classificationUuid
!= null){
989 queryString
+= "AND cls.uuid = :classificationUuid";
991 Query query
= getSession().createQuery(queryString
);
993 query
.setParameter("pattern", pattern
.toLowerCase()+"%");
994 query
.setParameter("classificationUuid", classificationUuid
);
997 @SuppressWarnings("unchecked")
998 List
<Object
[]> result
= query
.list();
999 Collections
.sort(result
, new UuidAndTitleCacheTaxonComparator());
1001 List
<TaxonNodeDto
> list
= new ArrayList
<>();
1002 for(Object
[] object
: result
){
1003 list
.add(new TaxonNodeDto((UUID
) object
[0],(Integer
) object
[1], (String
) object
[2]));
1009 public List
<UuidAndTitleCache
<TaxonNode
>> listChildNodesAsUuidAndTitleCache(UuidAndTitleCache
<TaxonNode
> parent
) {
1010 String queryString
=
1011 " SELECT tn.uuid, tn.id, t.titleCache "
1012 + " FROM TaxonNode tn "
1013 + " INNER JOIN tn.taxon AS t "
1014 + " WHERE tn.parent.uuid = :parentId";
1016 Query query
= getSession().createQuery(queryString
);
1017 query
.setParameter("parentId", parent
.getUuid());
1019 @SuppressWarnings("unchecked")
1020 List
<Object
[]> result
= query
.list();
1022 List
<UuidAndTitleCache
<TaxonNode
>> list
= new ArrayList
<>();
1023 for(Object
[] object
: result
){
1024 list
.add(new UuidAndTitleCache
<>((UUID
) object
[0],(Integer
) object
[1], (String
) object
[2]));