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
.Comparator
;
17 import java
.util
.HashMap
;
18 import java
.util
.HashSet
;
19 import java
.util
.Iterator
;
20 import java
.util
.List
;
23 import java
.util
.UUID
;
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
;
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
.Reference
;
43 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
44 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
45 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
46 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
47 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
48 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNodeAgentRelation
;
49 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
50 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.Restriction
;
51 import eu
.etaxonomy
.cdm
.persistence
.dao
.hibernate
.common
.AnnotatableDaoImpl
;
52 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.IClassificationDao
;
53 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonDao
;
54 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonNodeDao
;
55 import eu
.etaxonomy
.cdm
.persistence
.dao
.taxon
.ITaxonRelationshipDao
;
56 import eu
.etaxonomy
.cdm
.persistence
.dto
.SortableTaxonNodeQueryResult
;
57 import eu
.etaxonomy
.cdm
.persistence
.dto
.SortableTaxonNodeQueryResultComparator
;
58 import eu
.etaxonomy
.cdm
.persistence
.dto
.TaxonNodeDto
;
59 import eu
.etaxonomy
.cdm
.persistence
.dto
.UuidAndTitleCache
;
60 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
67 @Qualifier("taxonNodeDaoHibernateImpl")
68 public class TaxonNodeDaoHibernateImpl
extends AnnotatableDaoImpl
<TaxonNode
>
69 implements ITaxonNodeDao
{
71 private static final Logger logger
= Logger
.getLogger(TaxonNodeDaoHibernateImpl
.class);
74 private ITaxonDao taxonDao
;
76 private IClassificationDao classificationDao
;
78 private ITaxonRelationshipDao taxonRelDao
;
80 public TaxonNodeDaoHibernateImpl() {
81 super(TaxonNode
.class);
85 public UUID
delete(TaxonNode persistentObject
, boolean deleteChildren
){
86 Taxon taxon
= persistentObject
.getTaxon();
87 taxon
= HibernateProxyHelper
.deproxy(taxon
);
89 /*Session session = this.getSession();
90 Query query = session.createQuery("from TaxonNode t where t.taxon = :taxon");
91 query.setParameter("taxon", taxon);
92 List result = query.list();*/
94 Hibernate
.initialize(taxon
);
95 Hibernate
.initialize(taxon
.getTaxonNodes());
96 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
97 //Hibernate.initialize(taxon.getTaxonNodes());
98 for (TaxonNode node
:nodes
) {
99 node
= HibernateProxyHelper
.deproxy(node
);
101 if (node
.equals(persistentObject
)){
102 if (node
.hasChildNodes()){
103 Iterator
<TaxonNode
> childNodes
= node
.getChildNodes().iterator();
105 List
<TaxonNode
> listForDeletion
= new ArrayList
<>();
106 while (childNodes
.hasNext()){
107 childNode
= childNodes
.next();
108 listForDeletion
.add(childNode
);
112 for (TaxonNode deleteNode
:listForDeletion
){
113 delete(deleteNode
, deleteChildren
);
117 taxon
.removeTaxonNode(node
, deleteChildren
);
118 taxonDao
.saveOrUpdate(taxon
);
119 taxon
= HibernateProxyHelper
.deproxy(taxonDao
.findByUuid(taxon
.getUuid()), Taxon
.class);
120 taxonDao
.delete(taxon
);
126 UUID result
= super.delete(persistentObject
);
131 public List
<TaxonNode
> getTaxonOfAcceptedTaxaByClassification(Classification classification
, Integer start
, Integer end
) {
132 int classificationId
= classification
.getId();
134 if(start
!=null && end
!= null){
135 limit
= "LIMIT "+start
+"," +end
;
138 String queryString
= "SELECT DISTINCT nodes.*,taxa.titleCache "
139 + " FROM TaxonNode AS nodes "
140 + " LEFT JOIN TaxonBase AS taxa ON nodes.taxon_id = taxa.id "
141 + " WHERE taxa.DTYPE = 'Taxon' "
142 + " AND nodes.classification_id = " + classificationId
+
143 " ORDER BY taxa.titleCache " + limit
;
144 @SuppressWarnings("unchecked")
145 List
<TaxonNode
> result
= getSession().createSQLQuery(queryString
).addEntity(TaxonNode
.class).list();
151 public int countTaxonOfAcceptedTaxaByClassification(Classification classification
){
152 int classificationId
= classification
.getId();
154 String queryString
= ""
155 + " SELECT DISTINCT COUNT('nodes.*') "
156 + " FROM TaxonNode AS nodes "
157 + " LEFT JOIN TaxonBase AS taxa ON nodes.taxon_id = taxa.id "
158 + " WHERE taxa.DTYPE = 'Taxon' AND nodes.classification_id = " + classificationId
;
159 @SuppressWarnings("unchecked")
160 List
<BigInteger
> result
= getSession().createSQLQuery(queryString
).list();
161 return result
.get(0).intValue ();
165 public List
<TaxonNodeDto
> listChildNodesAsUuidAndTitleCache(TaxonNodeDto parent
) {
167 " SELECT tn.uuid, tn.id, t.titleCache "
168 + " FROM TaxonNode tn "
169 + " INNER JOIN tn.taxon AS t "
170 + " WHERE tn.parent.uuid = :parentId";
172 Query query
= getSession().createQuery(queryString
);
173 query
.setParameter("parentId", parent
.getUuid());
175 @SuppressWarnings("unchecked")
176 List
<Object
[]> result
= query
.list();
178 List
<TaxonNodeDto
> list
= new ArrayList
<>();
179 for(Object
[] object
: result
){
180 list
.add(new TaxonNodeDto((UUID
) object
[0],(Integer
) object
[1], (String
) object
[2]));
186 public List
<TaxonNodeDto
> listChildNodesAsTaxonNodeDto(TaxonNodeDto parent
) {
189 + " FROM TaxonNode tn "
190 + " INNER JOIN tn.taxon AS t "
191 + " WHERE tn.parent.uuid = :parentId";
192 Query query
= getSession().createQuery(queryString
);
193 query
.setParameter("parentId", parent
.getUuid());
195 @SuppressWarnings("unchecked")
196 List
<TaxonNode
> result
= query
.list();
198 List
<TaxonNodeDto
> list
= new ArrayList
<>();
199 for(TaxonNode object
: result
){
200 list
.add(new TaxonNodeDto(object
));
206 public List
<TaxonNodeDto
> getUuidAndTitleCache(Integer limit
, String pattern
, UUID classificationUuid
, boolean includeDoubtful
) {
208 Query query
= createQueryForUuidAndTitleCache(limit
, classificationUuid
, pattern
, includeDoubtful
);
209 @SuppressWarnings("unchecked")
210 List
<SortableTaxonNodeQueryResult
> result
= query
.list();
211 Collections
.sort(result
, new SortableTaxonNodeQueryResultComparator());
212 if(logger
.isTraceEnabled()){
213 logger
.trace("number of matches:" + result
.size());
214 result
.stream().forEach(o
-> logger
.trace("uuid: " + o
.getTaxonNodeUuid() + " titleCache:" + o
.getTaxonTitleCache() + " rank: " + o
.getNameRank()));
216 List
<TaxonNodeDto
> list
= new ArrayList
<>();
217 // int index = limit;
218 for(SortableTaxonNodeQueryResult stnqr
: result
){
220 list
.add(new TaxonNodeDto(stnqr
.getTaxonNodeUuid(),stnqr
.getTaxonNodeId(), stnqr
.getTaxonTitleCache()));
229 private Query
createQueryForUuidAndTitleCache(Integer limit
, UUID classificationUuid
, String pattern
, boolean includeDoubtful
){
230 String doubtfulPattern
= "";
231 String queryString
= "SELECT new " + SortableTaxonNodeQueryResult
.class.getName() + "("
232 + " node.uuid, node.id, t.titleCache, rank"
234 + " FROM TaxonNode AS node "
235 + " JOIN node.taxon as t " // FIXME why not inner join here?
236 + " INNER JOIN t.name AS name "
237 + " LEFT OUTER JOIN name.rank AS rank "
240 if (classificationUuid
!= null){
241 queryString
= queryString
+ " node.classification.uuid like :classificationUuid " ;
243 if (pattern
!= null){
244 if (pattern
.equals("?")){
247 if (!pattern
.endsWith("*")){
250 pattern
= pattern
.replace("*", "%");
251 pattern
= pattern
.replace("?", "%");
252 if (classificationUuid
!= null){
253 queryString
= queryString
+ " AND ";
255 queryString
= queryString
+ " (t.titleCache LIKE (:pattern) " ;
256 doubtfulPattern
= "?" + pattern
;
257 if (includeDoubtful
){
258 queryString
= queryString
+ " OR t.titleCache LIKE (:doubtfulPattern))";
260 queryString
= queryString
+ ")";
266 Query query
= getSession().createQuery(queryString
);
267 if (pattern
!= null){
268 query
.setParameter("pattern", pattern
);
270 if (includeDoubtful
){
271 query
.setParameter("doubtfulPattern", doubtfulPattern
);
274 if(classificationUuid
!= null){
275 query
.setParameter("classificationUuid", classificationUuid
);
278 query
.setMaxResults(limit
);
286 public TaxonNodeDto
getParentUuidAndTitleCache(TaxonNodeDto child
) {
287 String queryString
= ""
288 + " SELECT tn.parent.uuid, tn.parent.id, tn.parent.taxon.titleCache, "
289 + " tn.parent.classification.titleCache "
290 + " FROM TaxonNode tn"
291 + " LEFT OUTER JOIN tn.parent.taxon"
292 + " WHERE tn.id = :childId";
293 Query query
= getSession().createQuery(queryString
);
294 query
.setParameter("childId", child
.getId());
295 List
<TaxonNodeDto
> list
= new ArrayList
<>();
297 @SuppressWarnings("unchecked")
298 List
<Object
[]> result
= query
.list();
300 for(Object
[] object
: result
){
301 UUID uuid
= (UUID
) object
[0];
302 Integer id
= (Integer
) object
[1];
303 String taxonTitleCache
= (String
) object
[2];
304 String classificationTitleCache
= (String
) object
[3];
305 if(taxonTitleCache
!=null){
306 list
.add(new TaxonNodeDto(uuid
,id
, taxonTitleCache
));
309 list
.add(new TaxonNodeDto(uuid
,id
, classificationTitleCache
));
313 return list
.iterator().next();
318 public List
<TaxonNode
> listChildrenOf(TaxonNode node
, Integer pageSize
, Integer pageIndex
,
319 boolean recursive
, boolean includeUnpublished
, List
<String
> propertyPaths
, Comparator
<TaxonNode
> comparator
){
320 return listChildrenOfRecursive(node
,new ArrayList
<>(), pageSize
, pageIndex
, recursive
, includeUnpublished
, propertyPaths
, comparator
);
323 private List
<TaxonNode
> listChildrenOfRecursive(TaxonNode node
, List
<TaxonNode
> previousResult
, Integer pageSize
, Integer pageIndex
,
324 boolean recursive
, boolean includeUnpublished
, List
<String
> propertyPaths
, Comparator
<TaxonNode
> comparator
){
326 if (recursive
== true && comparator
== null ){
327 Criteria crit
= childrenOfCriteria(node
, includeUnpublished
);
329 this.addPageSizeAndNumber(crit
, pageSize
, pageIndex
);
330 @SuppressWarnings("unchecked")
331 List
<TaxonNode
> results
= crit
.list();
332 results
.remove(node
);
333 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
336 } else if (recursive
== true){
337 List
<TaxonNode
> children
= node
.getChildNodes();
338 Collections
.sort(children
, comparator
);
339 for (TaxonNode child
: children
){
340 if (!previousResult
.contains(child
)){
341 previousResult
.add(child
);
343 if (child
.hasChildNodes()){
344 previousResult
= listChildrenOfRecursive(child
, previousResult
, pageSize
, pageIndex
,
345 recursive
, includeUnpublished
, propertyPaths
, comparator
);
348 return previousResult
;
351 return classificationDao
.listChildrenOf(node
.getTaxon(), node
.getClassification(), null,
352 includeUnpublished
, pageSize
, pageIndex
, propertyPaths
);
357 public Long
countChildrenOf(TaxonNode node
, Classification classification
,
358 boolean recursive
, boolean includeUnpublished
) {
360 if (recursive
== true){
361 Criteria crit
= childrenOfCriteria(node
, includeUnpublished
);
362 crit
.setProjection(Projections
.rowCount());
363 return ((Integer
)crit
.uniqueResult().hashCode()).longValue();
365 return classificationDao
.countChildrenOf(
366 node
.getTaxon(), classification
, null, includeUnpublished
);
370 private Criteria
childrenOfCriteria(TaxonNode node
, boolean includeUnpublished
) {
371 Criteria crit
= getSession().createCriteria(TaxonNode
.class);
372 crit
.add( Restrictions
.like("treeIndex", node
.treeIndex()+ "%") );
373 if (!includeUnpublished
){
374 crit
.createCriteria("taxon").add( Restrictions
.eq("publish", Boolean
.TRUE
));
380 public List
<TaxonNodeAgentRelation
> listTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
,
381 UUID agentUuid
, UUID rankUuid
, UUID relTypeUuid
, Integer start
, Integer limit
,
382 List
<String
> propertyPaths
) {
384 StringBuilder hql
= prepareListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
,
385 agentUuid
, rankUuid
, relTypeUuid
, false);
387 Query query
= getSession().createQuery(hql
.toString());
390 query
.setMaxResults(limit
);
392 query
.setFirstResult(start
);
396 setParamsForListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
, agentUuid
, rankUuid
, relTypeUuid
, query
);
398 @SuppressWarnings("unchecked")
399 List
<TaxonNodeAgentRelation
> records
= query
.list();
401 if(propertyPaths
!= null) {
402 defaultBeanInitializer
.initializeAll(records
, propertyPaths
);
408 public <S
extends TaxonNode
> List
<S
> list(Class
<S
> type
, List
<Restriction
<?
>> restrictions
, Integer limit
,
409 Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
410 // TODO Auto-generated method stub
411 return list(type
, restrictions
, limit
, start
, orderHints
, propertyPaths
, INCLUDE_UNPUBLISHED
);
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
, boolean includePublished
) {
418 Criteria criteria
= createCriteria(type
, restrictions
, false);
420 if(!includePublished
){
421 criteria
.add(Restrictions
.eq("taxon.publish", true));
424 addLimitAndStart(criteria
, limit
, start
);
425 addOrder(criteria
, orderHints
);
427 @SuppressWarnings("unchecked")
428 List
<S
> result
= criteria
.list();
429 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
434 public long count(Class
<?
extends TaxonNode
> type
, List
<Restriction
<?
>> restrictions
) {
435 return count(type
, restrictions
, INCLUDE_UNPUBLISHED
);
440 public long count(Class
<?
extends TaxonNode
> type
, List
<Restriction
<?
>> restrictions
, boolean includePublished
) {
442 Criteria criteria
= createCriteria(type
, restrictions
, false);
443 if(!includePublished
){
444 criteria
.add(Restrictions
.eq("taxon.publish", true));
446 criteria
.setProjection(Projections
.projectionList().add(Projections
.rowCount()));
447 return (Long
) criteria
.uniqueResult();
451 public long countTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
, UUID agentUuid
, UUID rankUuid
, UUID relTypeUuid
) {
453 StringBuilder hql
= prepareListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
, agentUuid
, rankUuid
, relTypeUuid
, true);
454 Query query
= getSession().createQuery(hql
.toString());
456 setParamsForListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
, agentUuid
, rankUuid
, relTypeUuid
, query
);
458 Long count
= Long
.parseLong(query
.uniqueResult().toString());
465 * @param classificationUuid
467 * @param relTypeUuid TODO
468 * @param doCount TODO
470 * limit to taxa having this rank, only applies if <code>taxonUuid = null</code>
473 private StringBuilder
prepareListTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
, UUID agentUuid
, UUID rankUuid
, UUID relTypeUuid
, boolean doCount
) {
475 StringBuilder hql
= new StringBuilder();
477 String join_fetch_mode
= doCount ?
"JOIN" : "JOIN FETCH";
480 hql
.append("SELECT COUNT(tnar)");
482 hql
.append("SELECT tnar");
485 hql
.append(" FROM TaxonNodeAgentRelation AS tnar ");
486 if(taxonUuid
!= null) {
487 // taxonUuid is search filter, do not fetch it
488 hql
.append(" JOIN tnar.taxonNode AS tn "
489 + " JOIN tn.taxon AS t ");
491 hql
.append(join_fetch_mode
)
492 .append(" tnar.taxonNode AS tn ")
493 .append(join_fetch_mode
).append(" tn.taxon AS t ");
494 if(rankUuid
!= null) {
495 hql
.append(" join t.name as n ");
498 hql
.append(" JOIN tn.classification AS c ");
499 if(agentUuid
!= null) {
500 // agentUuid is search filter, do not fetch it
501 // hql.append(" join tnar.agent as a ");
502 hql
.append(join_fetch_mode
).append(" tnar.agent AS a ");
504 hql
.append(join_fetch_mode
).append(" tnar.agent AS a ");
507 hql
.append(" WHERE (1 = 1) ");
509 if(relTypeUuid
!= null) {
510 hql
.append(" AND tnar.type.uuid = :relTypeUuid ");
513 if(taxonUuid
!= null) {
514 hql
.append(" AND t.uuid = :taxonUuid ");
516 if(rankUuid
!= null) {
517 hql
.append(" AND n.rank.uuid = :rankUuid ");
520 if(classificationUuid
!= null) {
521 hql
.append(" AND c.uuid = :classificationUuid ");
523 if(agentUuid
!= null) {
524 hql
.append(" AND a.uuid = :agentUuid ");
527 hql
.append(" ORDER BY a.titleCache");
533 * @param classificationUuid
535 * @param relTypeUuid TODO
539 private void setParamsForListTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
, UUID agentUuid
,
540 UUID rankUuid
, UUID relTypeUuid
, Query query
) {
542 if(taxonUuid
!= null) {
543 query
.setParameter("taxonUuid", taxonUuid
);
545 if(rankUuid
!= null) {
546 query
.setParameter("rankUuid", rankUuid
);
549 if(classificationUuid
!= null) {
550 query
.setParameter("classificationUuid", classificationUuid
);
552 if(agentUuid
!= null) {
553 query
.setParameter("agentUuid", agentUuid
);
555 if(relTypeUuid
!= null) {
556 query
.setParameter("relTypeUuid", relTypeUuid
);
561 public Map
<TreeIndex
, Integer
> rankOrderIndexForTreeIndex(List
<TreeIndex
> treeIndexes
,
562 Integer minRankOrderIndex
,
563 Integer maxRankOrderIndex
) {
565 Map
<TreeIndex
, Integer
> result
= new HashMap
<>();
566 if (treeIndexes
== null || treeIndexes
.isEmpty()){
570 String hql
= " SELECT tn.treeIndex, r.orderIndex "
571 + " FROM TaxonNode tn "
572 + " JOIN tn.taxon t "
575 + " WHERE tn.treeIndex IN (:treeIndexes) ";
576 if (minRankOrderIndex
!= null){
577 hql
+= " AND r.orderIndex <= :minOrderIndex";
579 if (maxRankOrderIndex
!= null){
580 hql
+= " AND r.orderIndex >= :maxOrderIndex";
583 Query query
= getSession().createQuery(hql
);
584 query
.setParameterList("treeIndexes", TreeIndex
.toString(treeIndexes
));
585 if (minRankOrderIndex
!= null){
586 query
.setParameter("minOrderIndex", minRankOrderIndex
);
588 if (maxRankOrderIndex
!= null){
589 query
.setParameter("maxOrderIndex", maxRankOrderIndex
);
592 @SuppressWarnings("unchecked")
593 List
<Object
[]> list
= query
.list();
594 for (Object
[] o
: list
){
595 result
.put(TreeIndex
.NewInstance((String
)o
[0]), (Integer
)o
[1]);
601 public Map
<TreeIndex
, UuidAndTitleCache
<?
>> taxonUuidsForTreeIndexes(Collection
<TreeIndex
> treeIndexes
) {
602 Map
<TreeIndex
, UuidAndTitleCache
<?
>> result
= new HashMap
<>();
603 if (treeIndexes
== null || treeIndexes
.isEmpty()){
608 " SELECT tn.treeIndex, t.uuid, tnb.titleCache "
609 + " FROM TaxonNode tn JOIN tn.taxon t Join t.name tnb "
610 + " WHERE tn.treeIndex IN (:treeIndexes) ";
611 Query query
= getSession().createQuery(hql
);
612 query
.setParameterList("treeIndexes", TreeIndex
.toString(treeIndexes
));
614 @SuppressWarnings("unchecked")
615 List
<Object
[]> list
= query
.list();
616 for (Object
[] o
: list
){
617 result
.put(TreeIndex
.NewInstance((String
)o
[0]), new UuidAndTitleCache
<>((UUID
)o
[1], null, (String
)o
[2]));
623 public List
<TaxonNodeDto
> getParentTaxonNodeDtoForRank(
624 Classification classification
, Rank rank
, TaxonBase
<?
> taxonBase
) {
627 if (taxonBase
instanceof Taxon
) {
628 taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
630 taxon
= CdmBase
.deproxy(((Synonym
)taxonBase
).getAcceptedTaxon());
632 TaxonNode node
= null;
634 node
= taxon
.getTaxonNode(classification
);
636 List
<TaxonNodeDto
> result
= new ArrayList
<>();
638 String treeIndex
= node
.treeIndex();
639 List
<Integer
> ancestorNodeIds
= TreeIndex
.NewInstance(treeIndex
).parentNodeIds(false);
641 Criteria nodeCrit
= getSession().createCriteria(TaxonNode
.class);
642 Criteria taxonCrit
= nodeCrit
.createCriteria("taxon");
643 Criteria nameCrit
= taxonCrit
.createCriteria("name");
644 nodeCrit
.add(Restrictions
.in("id", ancestorNodeIds
));
645 nodeCrit
.add(Restrictions
.eq("classification", classification
));
646 nameCrit
.add(Restrictions
.eq("rank", rank
));
648 @SuppressWarnings("unchecked")
649 List
<TaxonNode
> list
= nodeCrit
.list();
650 for (TaxonNode rankNode
: list
){
651 TaxonNodeDto dto
= new TaxonNodeDto(rankNode
);
660 public List
<TaxonNodeDto
> getParentTaxonNodeDtoForRank(
661 Classification classification
, Rank rank
, TaxonName name
) {
663 Set
<TaxonBase
> taxa
= name
.getTaxonBases();
664 List
<TaxonNodeDto
> result
= new ArrayList
<>();
665 for (TaxonBase
<?
> taxonBase
:taxa
) {
666 List
<TaxonNodeDto
> tmpList
= getParentTaxonNodeDtoForRank(classification
, rank
, taxonBase
);
667 for (TaxonNodeDto tmpDto
: tmpList
){
668 boolean exists
= false; //an equal method does not yet exist for TaxonNodeDto therefore this workaround
669 for (TaxonNodeDto dto
: result
){
670 if (dto
.getTreeIndex().equals(tmpDto
.getTreeIndex())){
683 public int countSecundumForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, Reference newSec
,
684 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptySecundumDetail
) {
685 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.COUNT
);
686 if (!overwriteExisting
){
687 queryStr
+= " AND t.secSource.citation IS NULL ";
689 return countResult(queryStr
);
692 private int countResult(String queryStr
) {
693 Query query
= getSession().createQuery(queryStr
);
694 return ((Long
)query
.uniqueResult()).intValue();
698 public int countSecundumForSubtreeSynonyms(TreeIndex subTreeIndex
, Reference newSec
,
699 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptySecundumDetail
) {
700 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.COUNT
);
701 if (!overwriteExisting
){
702 queryStr
+= " AND syn.secSource.citation IS NULL ";
704 return countResult(queryStr
);
708 public int countSecundumForSubtreeRelations(TreeIndex subTreeIndex
, Reference newSec
,
709 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptySecundumDetail
) {
710 String queryStr
= forSubtreeRelationQueryStr(includeSharedTaxa
, overwriteExisting
, subTreeIndex
, SelectMode
.COUNT
);
711 return countResult(queryStr
);
716 public Set
<TaxonBase
> setSecundumForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, Reference newSec
,
717 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptyDetail
, IProgressMonitor monitor
) {
718 //for some reason this does not work, maybe because the listeners are not activated,
719 //but also the first taxon for some reason does not get updated in terms of secundum, but only by the update listener
720 // String where = "SELECT t.id FROM TaxonNode tn JOIN tn.taxon t " +
721 // " WHERE tn.treeIndex like '%s%%' ORDER BY t.id";
722 // where = String.format(where, subTreeIndex.toString());
723 // Query query1 = getSession().createQuery(where);
724 // List l = query1.list();
726 // String hql = "UPDATE Taxon SET sec = :newSec, publish=false WHERE id IN (" + where + ")";
727 // Query query = getSession().createQuery(hql);
728 // query.setParameter("newSec", newSec);
729 // int n = query.executeUpdate();
731 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.ID
);
732 if (!overwriteExisting
){
733 queryStr
+= " AND t.secSource.citation IS NULL ";
735 return setSecundum(newSec
, emptyDetail
, queryStr
, monitor
);
740 public Set
<TaxonBase
> setSecundumForSubtreeSynonyms(TreeIndex subTreeIndex
, Reference newSec
,
741 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptyDetail
, IProgressMonitor monitor
) {
743 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.ID
);
744 if (!overwriteExisting
){
745 queryStr
+= " AND syn.secSource.citation IS NULL ";
747 return setSecundum(newSec
, emptyDetail
, queryStr
, monitor
);
750 @SuppressWarnings("unchecked")
751 private <T
extends TaxonBase
<?
>> Set
<T
> setSecundum(Reference newSec
, boolean emptyDetail
, String queryStr
, IProgressMonitor monitor
) {
752 Set
<T
> result
= new HashSet
<>();
753 Query query
= getSession().createQuery(queryStr
);
754 List
<List
<Integer
>> partitionList
= splitIdList(query
.list(), DEFAULT_PARTITION_SIZE
);
755 for (List
<Integer
> taxonIdList
: partitionList
){
756 List
<TaxonBase
> taxonList
= taxonDao
.loadList(taxonIdList
, null, null);
757 for (TaxonBase
<?
> taxonBase
: taxonList
){
758 if (taxonBase
!= null){
759 taxonBase
= CdmBase
.deproxy(taxonBase
);
760 if (newSec
== null && taxonBase
.getSec() !=null
761 || newSec
!= null && (taxonBase
.getSec() == null || !newSec
.equals(taxonBase
.getSec()) )){
762 taxonBase
.setSec(newSec
);
763 result
.add((T
)taxonBase
);
766 if (taxonBase
.getSecMicroReference() != null){
767 taxonBase
.setSecMicroReference(null);
768 result
.add((T
)taxonBase
);
773 if (monitor
.isCanceled()){
783 public Set
<TaxonRelationship
> setSecundumForSubtreeRelations(TreeIndex subTreeIndex
, Reference newRef
,
784 Set
<UUID
> relationTypes
, boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptyDetail
, IProgressMonitor monitor
) {
786 String queryStr
= forSubtreeRelationQueryStr(includeSharedTaxa
, overwriteExisting
, subTreeIndex
, SelectMode
.ID
);
788 Set
<TaxonRelationship
> result
= new HashSet
<>();
789 Query query
= getSession().createQuery(queryStr
);
790 @SuppressWarnings("unchecked")
791 List
<List
<Integer
>> partitionList
= splitIdList(query
.list(), DEFAULT_PARTITION_SIZE
);
792 for (List
<Integer
> relIdList
: partitionList
){
793 List
<TaxonRelationship
> relList
= taxonRelDao
.loadList(relIdList
, null, null);
794 for (TaxonRelationship rel
: relList
){
796 rel
= CdmBase
.deproxy(rel
);
797 if (newRef
== null && rel
.getCitation() !=null
798 || newRef
!= null && (rel
.getCitation() == null || !newRef
.equals(rel
.getCitation()) )){
799 rel
.setCitation(newRef
);
803 if (rel
.getCitationMicroReference() != null){
804 rel
.setCitationMicroReference(null);
808 //TODO do we also need to add NamedSource to result?
810 if (monitor
.isCanceled()){
820 private List
<List
<Integer
>> splitIdList(List
<Integer
> idList
, Integer size
){
821 List
<List
<Integer
>> result
= new ArrayList
<>();
822 for (int i
= 0; (i
*size
)<idList
.size(); i
++) {
823 int upper
= Math
.min((i
+1)*size
, idList
.size());
824 result
.add(idList
.subList(i
*size
, upper
));
830 public int countPublishForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, boolean publish
, boolean includeSharedTaxa
, boolean includeHybrids
) {
831 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.COUNT
);
832 queryStr
+= " AND t.publish != :publish ";
833 System
.out
.println(queryStr
);
834 Query query
= getSession().createQuery(queryStr
);
835 query
.setBoolean("publish", publish
);
836 return ((Long
)query
.uniqueResult()).intValue();
840 public int countPublishForSubtreeSynonyms(TreeIndex subTreeIndex
, boolean publish
, boolean includeSharedTaxa
, boolean includeHybrids
) {
841 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.COUNT
);
842 queryStr
+= " AND syn.publish != :publish ";
843 Query query
= getSession().createQuery(queryStr
);
844 query
.setBoolean("publish", publish
);
845 return ((Long
)query
.uniqueResult()).intValue();
849 public Set
<TaxonBase
> setPublishForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, boolean publish
,
850 boolean includeSharedTaxa
, boolean includeHybrids
, IProgressMonitor monitor
) {
851 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.ID
);
852 queryStr
+= " AND t.publish != :publish ";
853 return setPublish(publish
, queryStr
, null, monitor
);
857 public Set
<TaxonBase
> setPublishForSubtreeSynonyms(TreeIndex subTreeIndex
, boolean publish
,
858 boolean includeSharedTaxa
, boolean includeHybrids
, IProgressMonitor monitor
) {
859 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.ID
);
860 queryStr
+= " AND syn.publish != :publish ";
861 return setPublish(publish
, queryStr
, null, monitor
);
865 public int countPublishForSubtreeRelatedTaxa(TreeIndex subTreeIndex
, boolean publish
, boolean includeSharedTaxa
, boolean includeHybrids
) {
866 String queryStr
= forSubtreeRelatedTaxaQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.COUNT
);
867 queryStr
+= " AND relTax.publish != :publish ";
868 Query query
= getSession().createQuery(queryStr
);
869 query
.setBoolean("publish", publish
);
870 return ((Long
)query
.uniqueResult()).intValue();
874 public Set
<TaxonBase
> setPublishForSubtreeRelatedTaxa(TreeIndex subTreeIndex
, boolean publish
,
875 Set
<UUID
> relationTypes
, boolean includeSharedTaxa
, boolean includeHybrids
,
876 IProgressMonitor monitor
) {
877 String queryStr
= forSubtreeRelatedTaxaQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.ID
);
878 queryStr
+= " AND relTax.publish != :publish ";
879 queryStr
+= " AND rel.type.uuid IN (:relTypeUuid)";
880 return setPublish(publish
, queryStr
, relationTypes
, monitor
);
883 private static final int DEFAULT_PARTITION_SIZE
= 100;
885 private <T
extends TaxonBase
<?
>> Set
<T
> setPublish(boolean publish
, String queryStr
, Set
<UUID
> relTypeUuids
, IProgressMonitor monitor
) {
886 Set
<T
> result
= new HashSet
<>();
887 Query query
= getSession().createQuery(queryStr
);
888 query
.setBoolean("publish", publish
);
889 if (relTypeUuids
!= null && !relTypeUuids
.isEmpty()){
890 query
.setParameterList("relTypeUuid", relTypeUuids
);
892 @SuppressWarnings("unchecked")
893 List
<List
<Integer
>> partitionList
= splitIdList(query
.list(), DEFAULT_PARTITION_SIZE
);
894 for (List
<Integer
> taxonIdList
: partitionList
){
895 List
<TaxonBase
> taxonList
= taxonDao
.loadList(taxonIdList
, null, null);
896 for (TaxonBase
<?
> taxonBase
: taxonList
){
897 if (taxonBase
!= null){
898 if (taxonBase
.isPublish() != publish
){ //to be on the save side
899 taxonBase
.setPublish(publish
);
900 result
.add((T
)CdmBase
.deproxy(taxonBase
));
903 if (monitor
.isCanceled()){
912 private String
forSubtreeSynonymQueryStr(boolean includeSharedTaxa
, TreeIndex subTreeIndex
, boolean excludeHybrids
, SelectMode mode
) {
913 String queryStr
= "SELECT " + mode
.hql("syn")
914 + " FROM TaxonNode tn "
915 + " JOIN tn.taxon t "
916 + " JOIN t.synonyms syn "
917 + " LEFT JOIN syn.name n "
918 + " LEFT JOIN syn.secSource ss ";
919 String whereStr
= " tn.treeIndex LIKE '%1$s%%' ";
920 if (!includeSharedTaxa
){
921 whereStr
+= " AND NOT EXISTS ("
922 + "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
924 whereStr
= handleExcludeHybrids(whereStr
, excludeHybrids
, "syn");
925 queryStr
+= " WHERE " + String
.format(whereStr
, subTreeIndex
.toString());
930 private String
handleExcludeHybrids(String whereStr
, boolean excludeHybrids
, String t
) {
933 String hybridWhere
= " AND (n is NULL OR "
934 + " (n.monomHybrid=0 AND n.binomHybrid=0 "
935 + " AND n.trinomHybrid=0 AND n.hybridFormula=0 )) ";
937 whereStr
+= hybridWhere
; //String.format(hybridWhere, t);
942 private String
forSubtreeRelatedTaxaQueryStr(boolean includeSharedTaxa
, TreeIndex subTreeIndex
,
943 boolean excludeHybrids
, SelectMode mode
) {
944 String queryStr
= "SELECT " + mode
.hql("relTax")
945 + " FROM TaxonNode tn "
946 + " JOIN tn.taxon t "
947 + " JOIN t.relationsToThisTaxon rel"
948 + " JOIN rel.relatedFrom relTax "
949 + " LEFT JOIN relTax.name n ";
950 String whereStr
=" tn.treeIndex LIKE '%1$s%%' ";
951 if (!includeSharedTaxa
){
952 //toTaxon should only be used in the given subtree
953 whereStr
+= " AND NOT EXISTS ("
954 + "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
955 //from taxon should not be used in another classification
956 whereStr
+= " AND NOT EXISTS ("
957 + "FROM TaxonNode tn3 WHERE tn3.taxon = relTax AND tn3.treeIndex not like '%1$s%%') ";
958 //fromTaxon should not be related as e.g. pro parte synonym or misapplication to
959 //another taxon which is not part of the subtree
960 //TODO and has not the publish state
961 whereStr
+= " AND NOT EXISTS ("
962 + "FROM TaxonNode tn4 JOIN tn4.taxon t2 JOIN t2.relationsToThisTaxon rel2 "
963 + " WHERE rel2.relatedFrom = relTax AND tn4.treeIndex not like '%1$s%%' "
964 + " AND tn4.taxon.publish != :publish ) ";
966 whereStr
= handleExcludeHybrids(whereStr
, excludeHybrids
, "relTax");
967 queryStr
+= " WHERE " + String
.format(whereStr
, subTreeIndex
.toString());
975 private String
forSubtreeRelationQueryStr(boolean includeSharedTaxa
, boolean overwriteExisting
,
976 TreeIndex subTreeIndex
, SelectMode mode
) {
978 String queryStr
= "SELECT " + mode
.hql("rel")
979 + " FROM TaxonNode tn "
980 + " JOIN tn.taxon t "
981 + " JOIN t.relationsToThisTaxon rel "
982 + " LEFT JOIN rel.source src ";
983 String whereStr
=" tn.treeIndex LIKE '%1$s%%' ";
984 if (!includeSharedTaxa
){
985 //toTaxon should only be used in the given subtree
986 whereStr
+= " AND NOT EXISTS ("
987 + "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
989 queryStr
+= " WHERE " + String
.format(whereStr
, subTreeIndex
.toString());
990 if (!overwriteExisting
){
991 queryStr
+= " AND (rel.source IS NULL OR src.citation IS NULL) ";
997 private enum SelectMode
{
1003 SelectMode(String hql
){
1006 public String
hql(String prefix
){
1010 return CdmUtils
.Nz(prefix
)+"." + hql
;
1012 return CdmUtils
.Nz(prefix
) + hql
;
1014 default: return hql
;
1020 private String
forSubtreeAcceptedQueryStr(boolean includeSharedTaxa
, TreeIndex subTreeIndex
, boolean excludeHybrids
, SelectMode mode
) {
1021 String queryStr
= "SELECT " + mode
.hql("t")
1022 + " FROM TaxonNode tn "
1023 + " JOIN tn.taxon t "
1024 + " LEFT JOIN t.name n "
1025 + " LEFT JOIN t.secSource ss ";
1026 String whereStr
= " tn.treeIndex like '%1$s%%' ";
1027 if (!includeSharedTaxa
){
1028 whereStr
+= " AND NOT EXISTS ("
1029 + "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
1031 whereStr
= handleExcludeHybrids(whereStr
, excludeHybrids
, "t");
1032 queryStr
+= " WHERE " + String
.format(whereStr
, subTreeIndex
.toString());
1038 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification
, Integer limit
, String pattern
, boolean searchForClassifications
, boolean includeDoubtful
) {
1040 Query query
= createQueryForUuidAndTitleCache(limit
, classification
.getUuid(), pattern
, includeDoubtful
);
1041 @SuppressWarnings("unchecked")
1042 List
<SortableTaxonNodeQueryResult
> result
= query
.list();
1045 if (searchForClassifications
){
1046 String queryString
= "SELECT new " + SortableTaxonNodeQueryResult
.class.getName() + "("
1047 + " node.uuid, node.id, node.classification.titleCache"
1049 + " FROM TaxonNode AS node "
1050 + " WHERE node.classification.id = " + classification
.getId() +
1051 " AND node.taxon IS NULL";
1052 if (pattern
!= null){
1053 if (pattern
.equals("?")){
1056 if (!pattern
.endsWith("*")){
1059 pattern
= pattern
.replace("*", "%");
1060 pattern
= pattern
.replace("?", "%");
1061 queryString
= queryString
+ " AND node.classification.titleCache LIKE (:pattern) " ;
1064 query
= getSession().createQuery(queryString
);
1067 query
.setMaxResults(limit
);
1070 if (pattern
!= null && !pattern
.equals("?")){
1071 query
.setParameter("pattern", pattern
);
1073 @SuppressWarnings("unchecked")
1074 List
<SortableTaxonNodeQueryResult
> resultClassifications
= query
.list();
1076 result
.addAll(resultClassifications
);
1079 if(result
.size() == 0){
1082 List
<UuidAndTitleCache
<TaxonNode
>> list
= new ArrayList
<>(result
.size());
1083 Collections
.sort(result
, new SortableTaxonNodeQueryResultComparator());
1084 for (SortableTaxonNodeQueryResult resultDTO
: result
){
1085 list
.add(new UuidAndTitleCache
<>(TaxonNode
.class, resultDTO
.getTaxonNodeUuid(), resultDTO
.getTaxonNodeId(), resultDTO
.getTaxonTitleCache()));
1093 public List
<TaxonNodeDto
> getTaxonNodeDto(Integer limit
, String pattern
, UUID classificationUuid
) {
1094 String queryString
= "SELECT new " + SortableTaxonNodeQueryResult
.class.getName() + "("
1095 + "tn.uuid, tn.id, t.titleCache, rank "
1097 + " FROM TaxonNode tn "
1098 + " INNER JOIN tn.taxon AS t "
1099 + " INNER JOIN tn.classification AS cls "
1100 + " INNER JOIN t.name AS name "
1101 + " LEFT OUTER JOIN name.rank AS rank "
1102 + " WHERE t.titleCache LIKE :pattern ";
1103 if(classificationUuid
!= null){
1104 queryString
+= "AND cls.uuid = :classificationUuid";
1107 Query query
= getSession().createQuery(queryString
);
1109 query
.setParameter("pattern", pattern
.toLowerCase()+"%");
1110 if(classificationUuid
!= null){
1111 query
.setParameter("classificationUuid", classificationUuid
);
1114 @SuppressWarnings("unchecked")
1115 List
<SortableTaxonNodeQueryResult
> result
= query
.list();
1116 Collections
.sort(result
, new SortableTaxonNodeQueryResultComparator());
1118 List
<TaxonNodeDto
> list
= new ArrayList
<>();
1119 for(SortableTaxonNodeQueryResult queryDTO
: result
){
1120 list
.add(new TaxonNodeDto(queryDTO
.getTaxonNodeUuid(), queryDTO
.getTaxonNodeId(), queryDTO
.getTaxonTitleCache()));
1126 public List
<TaxonNodeDto
> getUuidAndTitleCache(Integer limit
, String pattern
, UUID classificationUuid
) {
1127 return getUuidAndTitleCache(limit
, pattern
, classificationUuid
, false);
1131 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1132 Classification classification
, Integer limit
, String pattern
, boolean searchForClassifications
) {
1133 return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification
, limit
, pattern
, searchForClassifications
, false);