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);
73 private static final int DEFAULT_SET_SUBTREE_PARTITION_SIZE
= 100;
76 private ITaxonDao taxonDao
;
78 private IClassificationDao classificationDao
;
80 private ITaxonRelationshipDao taxonRelDao
;
82 public TaxonNodeDaoHibernateImpl() {
83 super(TaxonNode
.class);
87 public UUID
delete(TaxonNode persistentObject
, boolean deleteChildren
){
88 Taxon taxon
= persistentObject
.getTaxon();
89 taxon
= HibernateProxyHelper
.deproxy(taxon
);
91 /*Session session = this.getSession();
92 Query query = session.createQuery("from TaxonNode t where t.taxon = :taxon");
93 query.setParameter("taxon", taxon);
94 List result = query.list();*/
96 Hibernate
.initialize(taxon
);
97 Hibernate
.initialize(taxon
.getTaxonNodes());
98 Set
<TaxonNode
> nodes
= taxon
.getTaxonNodes();
99 //Hibernate.initialize(taxon.getTaxonNodes());
100 for (TaxonNode node
:nodes
) {
101 node
= HibernateProxyHelper
.deproxy(node
);
103 if (node
.equals(persistentObject
)){
104 if (node
.hasChildNodes()){
105 Iterator
<TaxonNode
> childNodes
= node
.getChildNodes().iterator();
107 List
<TaxonNode
> listForDeletion
= new ArrayList
<>();
108 while (childNodes
.hasNext()){
109 childNode
= childNodes
.next();
110 listForDeletion
.add(childNode
);
114 for (TaxonNode deleteNode
:listForDeletion
){
115 delete(deleteNode
, deleteChildren
);
119 taxon
.removeTaxonNode(node
, deleteChildren
);
120 taxonDao
.saveOrUpdate(taxon
);
121 taxon
= HibernateProxyHelper
.deproxy(taxonDao
.findByUuid(taxon
.getUuid()), Taxon
.class);
122 taxonDao
.delete(taxon
);
128 UUID result
= super.delete(persistentObject
);
133 public List
<TaxonNode
> getTaxonOfAcceptedTaxaByClassification(Classification classification
, Integer start
, Integer end
) {
134 int classificationId
= classification
.getId();
136 if(start
!=null && end
!= null){
137 limit
= "LIMIT "+start
+"," +end
;
140 String queryString
= "SELECT DISTINCT nodes.*,taxa.titleCache "
141 + " FROM TaxonNode AS nodes "
142 + " LEFT JOIN TaxonBase AS taxa ON nodes.taxon_id = taxa.id "
143 + " WHERE taxa.DTYPE = 'Taxon' "
144 + " AND nodes.classification_id = " + classificationId
+
145 " ORDER BY taxa.titleCache " + limit
;
146 @SuppressWarnings("unchecked")
147 List
<TaxonNode
> result
= getSession().createSQLQuery(queryString
).addEntity(TaxonNode
.class).list();
153 public int countTaxonOfAcceptedTaxaByClassification(Classification classification
){
154 int classificationId
= classification
.getId();
156 String queryString
= ""
157 + " SELECT DISTINCT COUNT('nodes.*') "
158 + " FROM TaxonNode AS nodes "
159 + " LEFT JOIN TaxonBase AS taxa ON nodes.taxon_id = taxa.id "
160 + " WHERE taxa.DTYPE = 'Taxon' AND nodes.classification_id = " + classificationId
;
161 @SuppressWarnings("unchecked")
162 List
<BigInteger
> result
= getSession().createSQLQuery(queryString
).list();
163 return result
.get(0).intValue ();
167 public List
<TaxonNodeDto
> listChildNodesAsUuidAndTitleCache(TaxonNodeDto parent
) {
169 " SELECT tn.uuid, tn.id, t.titleCache "
170 + " FROM TaxonNode tn "
171 + " INNER JOIN tn.taxon AS t "
172 + " WHERE tn.parent.uuid = :parentId";
174 Query query
= getSession().createQuery(queryString
);
175 query
.setParameter("parentId", parent
.getUuid());
177 @SuppressWarnings("unchecked")
178 List
<Object
[]> result
= query
.list();
180 List
<TaxonNodeDto
> list
= new ArrayList
<>();
181 for(Object
[] object
: result
){
182 list
.add(new TaxonNodeDto((UUID
) object
[0],(Integer
) object
[1], (String
) object
[2]));
188 public List
<TaxonNodeDto
> listChildNodesAsTaxonNodeDto(TaxonNodeDto parent
) {
191 + " FROM TaxonNode tn "
192 + " INNER JOIN tn.taxon AS t "
193 + " WHERE tn.parent.uuid = :parentId";
194 Query query
= getSession().createQuery(queryString
);
195 query
.setParameter("parentId", parent
.getUuid());
197 @SuppressWarnings("unchecked")
198 List
<TaxonNode
> result
= query
.list();
200 List
<TaxonNodeDto
> list
= new ArrayList
<>();
201 for(TaxonNode object
: result
){
202 list
.add(new TaxonNodeDto(object
));
208 public List
<TaxonNodeDto
> getUuidAndTitleCache(Integer limit
, String pattern
, UUID classificationUuid
, boolean includeDoubtful
) {
210 Query query
= createQueryForUuidAndTitleCache(limit
, classificationUuid
, pattern
, includeDoubtful
);
211 @SuppressWarnings("unchecked")
212 List
<SortableTaxonNodeQueryResult
> result
= query
.list();
213 Collections
.sort(result
, new SortableTaxonNodeQueryResultComparator());
214 if(logger
.isTraceEnabled()){
215 logger
.trace("number of matches:" + result
.size());
216 result
.stream().forEach(o
-> logger
.trace("uuid: " + o
.getTaxonNodeUuid() + " titleCache:" + o
.getTaxonTitleCache() + " rank: " + o
.getNameRank()));
218 List
<TaxonNodeDto
> list
= new ArrayList
<>();
219 // int index = limit;
220 for(SortableTaxonNodeQueryResult stnqr
: result
){
222 list
.add(new TaxonNodeDto(stnqr
.getTaxonNodeUuid(),stnqr
.getTaxonNodeId(), stnqr
.getTaxonTitleCache()));
231 private Query
createQueryForUuidAndTitleCache(Integer limit
, UUID classificationUuid
, String pattern
, boolean includeDoubtful
){
232 String doubtfulPattern
= "";
233 String queryString
= "SELECT new " + SortableTaxonNodeQueryResult
.class.getName() + "("
234 + " node.uuid, node.id, t.titleCache, rank"
236 + " FROM TaxonNode AS node "
237 + " JOIN node.taxon as t " // FIXME why not inner join here?
238 + " INNER JOIN t.name AS name "
239 + " LEFT OUTER JOIN name.rank AS rank "
242 if (classificationUuid
!= null){
243 queryString
= queryString
+ " node.classification.uuid like :classificationUuid " ;
245 if (pattern
!= null){
246 if (pattern
.equals("?")){
249 if (!pattern
.endsWith("*")){
252 pattern
= pattern
.replace("*", "%");
253 pattern
= pattern
.replace("?", "%");
254 if (classificationUuid
!= null){
255 queryString
= queryString
+ " AND ";
257 queryString
= queryString
+ " (t.titleCache LIKE (:pattern) " ;
258 doubtfulPattern
= "?" + pattern
;
259 if (includeDoubtful
){
260 queryString
= queryString
+ " OR t.titleCache LIKE (:doubtfulPattern))";
262 queryString
= queryString
+ ")";
268 Query query
= getSession().createQuery(queryString
);
269 if (pattern
!= null){
270 query
.setParameter("pattern", pattern
);
272 if (includeDoubtful
){
273 query
.setParameter("doubtfulPattern", doubtfulPattern
);
276 if(classificationUuid
!= null){
277 query
.setParameter("classificationUuid", classificationUuid
);
280 query
.setMaxResults(limit
);
288 public TaxonNodeDto
getParentUuidAndTitleCache(TaxonNodeDto child
) {
289 String queryString
= ""
290 + " SELECT tn.parent.uuid, tn.parent.id, tn.parent.taxon.titleCache, "
291 + " tn.parent.classification.titleCache "
292 + " FROM TaxonNode tn"
293 + " LEFT OUTER JOIN tn.parent.taxon"
294 + " WHERE tn.id = :childId";
295 Query query
= getSession().createQuery(queryString
);
296 query
.setParameter("childId", child
.getId());
297 List
<TaxonNodeDto
> list
= new ArrayList
<>();
299 @SuppressWarnings("unchecked")
300 List
<Object
[]> result
= query
.list();
302 for(Object
[] object
: result
){
303 UUID uuid
= (UUID
) object
[0];
304 Integer id
= (Integer
) object
[1];
305 String taxonTitleCache
= (String
) object
[2];
306 String classificationTitleCache
= (String
) object
[3];
307 if(taxonTitleCache
!=null){
308 list
.add(new TaxonNodeDto(uuid
,id
, taxonTitleCache
));
311 list
.add(new TaxonNodeDto(uuid
,id
, classificationTitleCache
));
315 return list
.iterator().next();
320 public List
<TaxonNode
> listChildrenOf(TaxonNode node
, Integer pageSize
, Integer pageIndex
,
321 boolean recursive
, boolean includeUnpublished
, List
<String
> propertyPaths
, Comparator
<TaxonNode
> comparator
){
322 return listChildrenOfRecursive(node
,new ArrayList
<>(), pageSize
, pageIndex
, recursive
, includeUnpublished
, propertyPaths
, comparator
);
325 private List
<TaxonNode
> listChildrenOfRecursive(TaxonNode node
, List
<TaxonNode
> previousResult
, Integer pageSize
, Integer pageIndex
,
326 boolean recursive
, boolean includeUnpublished
, List
<String
> propertyPaths
, Comparator
<TaxonNode
> comparator
){
328 if (recursive
== true && comparator
== null ){
329 Criteria crit
= childrenOfCriteria(node
, includeUnpublished
);
331 this.addPageSizeAndNumber(crit
, pageSize
, pageIndex
);
332 @SuppressWarnings("unchecked")
333 List
<TaxonNode
> results
= crit
.list();
334 results
.remove(node
);
335 defaultBeanInitializer
.initializeAll(results
, propertyPaths
);
338 } else if (recursive
== true){
339 List
<TaxonNode
> children
= node
.getChildNodes();
340 Collections
.sort(children
, comparator
);
341 for (TaxonNode child
: children
){
342 if (!previousResult
.contains(child
)){
343 previousResult
.add(child
);
345 if (child
.hasChildNodes()){
346 previousResult
= listChildrenOfRecursive(child
, previousResult
, pageSize
, pageIndex
,
347 recursive
, includeUnpublished
, propertyPaths
, comparator
);
350 return previousResult
;
353 return classificationDao
.listChildrenOf(node
.getTaxon(), node
.getClassification(), null,
354 includeUnpublished
, pageSize
, pageIndex
, propertyPaths
);
359 public Long
countChildrenOf(TaxonNode node
, Classification classification
,
360 boolean recursive
, boolean includeUnpublished
) {
362 if (recursive
== true){
363 Criteria crit
= childrenOfCriteria(node
, includeUnpublished
);
364 crit
.setProjection(Projections
.rowCount());
365 return ((Integer
)crit
.uniqueResult().hashCode()).longValue();
367 return classificationDao
.countChildrenOf(
368 node
.getTaxon(), classification
, null, includeUnpublished
);
372 private Criteria
childrenOfCriteria(TaxonNode node
, boolean includeUnpublished
) {
373 Criteria crit
= getSession().createCriteria(TaxonNode
.class);
374 crit
.add( Restrictions
.like("treeIndex", node
.treeIndex()+ "%") );
375 if (!includeUnpublished
){
376 crit
.createCriteria("taxon").add( Restrictions
.eq("publish", Boolean
.TRUE
));
382 public List
<TaxonNodeAgentRelation
> listTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
,
383 UUID agentUuid
, UUID rankUuid
, UUID relTypeUuid
, Integer start
, Integer limit
,
384 List
<String
> propertyPaths
) {
386 StringBuilder hql
= prepareListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
,
387 agentUuid
, rankUuid
, relTypeUuid
, false);
389 Query query
= getSession().createQuery(hql
.toString());
392 query
.setMaxResults(limit
);
394 query
.setFirstResult(start
);
398 setParamsForListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
, agentUuid
, rankUuid
, relTypeUuid
, query
);
400 @SuppressWarnings("unchecked")
401 List
<TaxonNodeAgentRelation
> records
= query
.list();
403 if(propertyPaths
!= null) {
404 defaultBeanInitializer
.initializeAll(records
, propertyPaths
);
410 public <S
extends TaxonNode
> List
<S
> list(Class
<S
> type
, List
<Restriction
<?
>> restrictions
, Integer limit
,
411 Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
412 // TODO Auto-generated method stub
413 return list(type
, restrictions
, limit
, start
, orderHints
, propertyPaths
, INCLUDE_UNPUBLISHED
);
417 public <S
extends TaxonNode
> List
<S
> list(Class
<S
> type
, List
<Restriction
<?
>> restrictions
, Integer limit
,
418 Integer start
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
, boolean includePublished
) {
420 Criteria criteria
= createCriteria(type
, restrictions
, false);
422 if(!includePublished
){
423 criteria
.add(Restrictions
.eq("taxon.publish", true));
426 addLimitAndStart(criteria
, limit
, start
);
427 addOrder(criteria
, orderHints
);
429 @SuppressWarnings("unchecked")
430 List
<S
> result
= criteria
.list();
431 defaultBeanInitializer
.initializeAll(result
, propertyPaths
);
436 public long count(Class
<?
extends TaxonNode
> type
, List
<Restriction
<?
>> restrictions
) {
437 return count(type
, restrictions
, INCLUDE_UNPUBLISHED
);
442 public long count(Class
<?
extends TaxonNode
> type
, List
<Restriction
<?
>> restrictions
, boolean includePublished
) {
444 Criteria criteria
= createCriteria(type
, restrictions
, false);
445 if(!includePublished
){
446 criteria
.add(Restrictions
.eq("taxon.publish", true));
448 criteria
.setProjection(Projections
.projectionList().add(Projections
.rowCount()));
449 return (Long
) criteria
.uniqueResult();
453 public long countTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
, UUID agentUuid
, UUID rankUuid
, UUID relTypeUuid
) {
455 StringBuilder hql
= prepareListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
, agentUuid
, rankUuid
, relTypeUuid
, true);
456 Query query
= getSession().createQuery(hql
.toString());
458 setParamsForListTaxonNodeAgentRelations(taxonUuid
, classificationUuid
, agentUuid
, rankUuid
, relTypeUuid
, query
);
460 Long count
= Long
.parseLong(query
.uniqueResult().toString());
467 * @param classificationUuid
469 * @param relTypeUuid TODO
470 * @param doCount TODO
472 * limit to taxa having this rank, only applies if <code>taxonUuid = null</code>
475 private StringBuilder
prepareListTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
, UUID agentUuid
, UUID rankUuid
, UUID relTypeUuid
, boolean doCount
) {
477 StringBuilder hql
= new StringBuilder();
479 String join_fetch_mode
= doCount ?
"JOIN" : "JOIN FETCH";
482 hql
.append("SELECT COUNT(tnar)");
484 hql
.append("SELECT tnar");
487 hql
.append(" FROM TaxonNodeAgentRelation AS tnar ");
488 if(taxonUuid
!= null) {
489 // taxonUuid is search filter, do not fetch it
490 hql
.append(" JOIN tnar.taxonNode AS tn "
491 + " JOIN tn.taxon AS t ");
493 hql
.append(join_fetch_mode
)
494 .append(" tnar.taxonNode AS tn ")
495 .append(join_fetch_mode
).append(" tn.taxon AS t ");
496 if(rankUuid
!= null) {
497 hql
.append(" join t.name as n ");
500 hql
.append(" JOIN tn.classification AS c ");
501 if(agentUuid
!= null) {
502 // agentUuid is search filter, do not fetch it
503 // hql.append(" join tnar.agent as a ");
504 hql
.append(join_fetch_mode
).append(" tnar.agent AS a ");
506 hql
.append(join_fetch_mode
).append(" tnar.agent AS a ");
509 hql
.append(" WHERE (1 = 1) ");
511 if(relTypeUuid
!= null) {
512 hql
.append(" AND tnar.type.uuid = :relTypeUuid ");
515 if(taxonUuid
!= null) {
516 hql
.append(" AND t.uuid = :taxonUuid ");
518 if(rankUuid
!= null) {
519 hql
.append(" AND n.rank.uuid = :rankUuid ");
522 if(classificationUuid
!= null) {
523 hql
.append(" AND c.uuid = :classificationUuid ");
525 if(agentUuid
!= null) {
526 hql
.append(" AND a.uuid = :agentUuid ");
529 hql
.append(" ORDER BY a.titleCache");
535 * @param classificationUuid
537 * @param relTypeUuid TODO
541 private void setParamsForListTaxonNodeAgentRelations(UUID taxonUuid
, UUID classificationUuid
, UUID agentUuid
,
542 UUID rankUuid
, UUID relTypeUuid
, Query query
) {
544 if(taxonUuid
!= null) {
545 query
.setParameter("taxonUuid", taxonUuid
);
547 if(rankUuid
!= null) {
548 query
.setParameter("rankUuid", rankUuid
);
551 if(classificationUuid
!= null) {
552 query
.setParameter("classificationUuid", classificationUuid
);
554 if(agentUuid
!= null) {
555 query
.setParameter("agentUuid", agentUuid
);
557 if(relTypeUuid
!= null) {
558 query
.setParameter("relTypeUuid", relTypeUuid
);
563 public Map
<TreeIndex
, Integer
> rankOrderIndexForTreeIndex(List
<TreeIndex
> treeIndexes
,
564 Integer minRankOrderIndex
,
565 Integer maxRankOrderIndex
) {
567 Map
<TreeIndex
, Integer
> result
= new HashMap
<>();
568 if (treeIndexes
== null || treeIndexes
.isEmpty()){
572 String hql
= " SELECT tn.treeIndex, r.orderIndex "
573 + " FROM TaxonNode tn "
574 + " JOIN tn.taxon t "
577 + " WHERE tn.treeIndex IN (:treeIndexes) ";
578 if (minRankOrderIndex
!= null){
579 hql
+= " AND r.orderIndex <= :minOrderIndex";
581 if (maxRankOrderIndex
!= null){
582 hql
+= " AND r.orderIndex >= :maxOrderIndex";
585 Query query
= getSession().createQuery(hql
);
586 query
.setParameterList("treeIndexes", TreeIndex
.toString(treeIndexes
));
587 if (minRankOrderIndex
!= null){
588 query
.setParameter("minOrderIndex", minRankOrderIndex
);
590 if (maxRankOrderIndex
!= null){
591 query
.setParameter("maxOrderIndex", maxRankOrderIndex
);
594 @SuppressWarnings("unchecked")
595 List
<Object
[]> list
= query
.list();
596 for (Object
[] o
: list
){
597 result
.put(TreeIndex
.NewInstance((String
)o
[0]), (Integer
)o
[1]);
603 public Map
<TreeIndex
, UuidAndTitleCache
<?
>> taxonUuidsForTreeIndexes(Collection
<TreeIndex
> treeIndexes
) {
604 Map
<TreeIndex
, UuidAndTitleCache
<?
>> result
= new HashMap
<>();
605 if (treeIndexes
== null || treeIndexes
.isEmpty()){
610 " SELECT tn.treeIndex, t.uuid, tnb.titleCache "
611 + " FROM TaxonNode tn JOIN tn.taxon t Join t.name tnb "
612 + " WHERE tn.treeIndex IN (:treeIndexes) ";
613 Query query
= getSession().createQuery(hql
);
614 query
.setParameterList("treeIndexes", TreeIndex
.toString(treeIndexes
));
616 @SuppressWarnings("unchecked")
617 List
<Object
[]> list
= query
.list();
618 for (Object
[] o
: list
){
619 result
.put(TreeIndex
.NewInstance((String
)o
[0]), new UuidAndTitleCache
<>((UUID
)o
[1], null, (String
)o
[2]));
625 public List
<TaxonNodeDto
> getParentTaxonNodeDtoForRank(
626 Classification classification
, Rank rank
, TaxonBase
<?
> taxonBase
) {
629 if (taxonBase
instanceof Taxon
) {
630 taxon
= CdmBase
.deproxy(taxonBase
, Taxon
.class);
632 taxon
= CdmBase
.deproxy(((Synonym
)taxonBase
).getAcceptedTaxon());
634 TaxonNode node
= null;
636 node
= taxon
.getTaxonNode(classification
);
638 List
<TaxonNodeDto
> result
= new ArrayList
<>();
640 String treeIndex
= node
.treeIndex();
641 List
<Integer
> ancestorNodeIds
= TreeIndex
.NewInstance(treeIndex
).parentNodeIds(false);
643 Criteria nodeCrit
= getSession().createCriteria(TaxonNode
.class);
644 Criteria taxonCrit
= nodeCrit
.createCriteria("taxon");
645 Criteria nameCrit
= taxonCrit
.createCriteria("name");
646 nodeCrit
.add(Restrictions
.in("id", ancestorNodeIds
));
647 nodeCrit
.add(Restrictions
.eq("classification", classification
));
648 nameCrit
.add(Restrictions
.eq("rank", rank
));
650 @SuppressWarnings("unchecked")
651 List
<TaxonNode
> list
= nodeCrit
.list();
652 for (TaxonNode rankNode
: list
){
653 TaxonNodeDto dto
= new TaxonNodeDto(rankNode
);
662 public List
<TaxonNodeDto
> getParentTaxonNodeDtoForRank(
663 Classification classification
, Rank rank
, TaxonName name
) {
665 Set
<TaxonBase
> taxa
= name
.getTaxonBases();
666 List
<TaxonNodeDto
> result
= new ArrayList
<>();
667 for (TaxonBase
<?
> taxonBase
:taxa
) {
668 List
<TaxonNodeDto
> tmpList
= getParentTaxonNodeDtoForRank(classification
, rank
, taxonBase
);
669 for (TaxonNodeDto tmpDto
: tmpList
){
670 boolean exists
= false; //an equal method does not yet exist for TaxonNodeDto therefore this workaround
671 for (TaxonNodeDto dto
: result
){
672 if (dto
.getTreeIndex().equals(tmpDto
.getTreeIndex())){
685 public int countSecundumForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, Reference newSec
,
686 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptySecundumDetail
) {
687 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.COUNT
);
688 if (!overwriteExisting
){
689 queryStr
+= " AND t.secSource.citation IS NULL ";
691 return countResult(queryStr
);
694 private int countResult(String queryStr
) {
695 Query query
= getSession().createQuery(queryStr
);
696 return ((Long
)query
.uniqueResult()).intValue();
700 public int countSecundumForSubtreeSynonyms(TreeIndex subTreeIndex
, Reference newSec
,
701 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptySecundumDetail
) {
702 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.COUNT
);
703 if (!overwriteExisting
){
704 queryStr
+= " AND syn.secSource.citation IS NULL ";
706 return countResult(queryStr
);
710 public int countSecundumForSubtreeRelations(TreeIndex subTreeIndex
, Reference newSec
,
711 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptySecundumDetail
) {
712 String queryStr
= forSubtreeRelationQueryStr(includeSharedTaxa
, overwriteExisting
, subTreeIndex
, SelectMode
.COUNT
);
713 return countResult(queryStr
);
718 public Set
<TaxonBase
> setSecundumForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, Reference newSec
,
719 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptyDetail
, IProgressMonitor monitor
) {
720 //for some reason this does not work, maybe because the listeners are not activated,
721 //but also the first taxon for some reason does not get updated in terms of secundum, but only by the update listener
722 // String where = "SELECT t.id FROM TaxonNode tn JOIN tn.taxon t " +
723 // " WHERE tn.treeIndex like '%s%%' ORDER BY t.id";
724 // where = String.format(where, subTreeIndex.toString());
725 // Query query1 = getSession().createQuery(where);
726 // List l = query1.list();
728 // String hql = "UPDATE Taxon SET sec = :newSec, publish=false WHERE id IN (" + where + ")";
729 // Query query = getSession().createQuery(hql);
730 // query.setParameter("newSec", newSec);
731 // int n = query.executeUpdate();
733 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.ID
);
734 if (!overwriteExisting
){
735 queryStr
+= " AND t.secSource.citation IS NULL ";
737 return setSecundum(newSec
, emptyDetail
, queryStr
, monitor
);
742 public Set
<TaxonBase
> setSecundumForSubtreeSynonyms(TreeIndex subTreeIndex
, Reference newSec
,
743 boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptyDetail
, IProgressMonitor monitor
) {
745 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, false, SelectMode
.ID
);
746 if (!overwriteExisting
){
747 queryStr
+= " AND syn.secSource.citation IS NULL ";
749 return setSecundum(newSec
, emptyDetail
, queryStr
, monitor
);
752 private <T
extends TaxonBase
<?
>> Set
<T
> setSecundum(Reference newSec
, boolean emptyDetail
, String queryStr
, IProgressMonitor monitor
) {
753 Set
<T
> result
= new HashSet
<>();
754 Query query
= getSession().createQuery(queryStr
);
755 @SuppressWarnings("unchecked")
756 List
<List
<Integer
>> partitionList
= splitIdList(query
.list(), DEFAULT_SET_SUBTREE_PARTITION_SIZE
);
757 for (List
<Integer
> taxonIdList
: partitionList
){
758 @SuppressWarnings("unchecked")
759 List
<T
> taxonList
= (List
<T
>)taxonDao
.loadList(taxonIdList
, null, null);
760 for (T taxonBase
: taxonList
){
761 if (taxonBase
!= null){
762 taxonBase
= CdmBase
.deproxy(taxonBase
);
763 if (newSec
== null && taxonBase
.getSec() !=null
764 || newSec
!= null && (taxonBase
.getSec() == null || !newSec
.equals(taxonBase
.getSec()) )){
765 taxonBase
.setSec(newSec
);
766 result
.add(taxonBase
);
769 if (taxonBase
.getSecMicroReference() != null){
770 taxonBase
.setSecMicroReference(null);
771 result
.add(taxonBase
);
776 if (monitor
.isCanceled()){
781 commitAndRestartTransaction(newSec
);
786 private void commitAndRestartTransaction(CdmBase
... cdmBaseToUpdate
) {
787 getSession().getTransaction().commit();
788 getSession().beginTransaction();
789 for (CdmBase cdmBase
: cdmBaseToUpdate
){
790 getSession().update(cdmBase
);
795 public Set
<TaxonRelationship
> setSecundumForSubtreeRelations(TreeIndex subTreeIndex
, Reference newRef
,
796 Set
<UUID
> relationTypes
, boolean overwriteExisting
, boolean includeSharedTaxa
, boolean emptyDetail
, IProgressMonitor monitor
) {
798 String queryStr
= forSubtreeRelationQueryStr(includeSharedTaxa
, overwriteExisting
, subTreeIndex
, SelectMode
.ID
);
800 Set
<TaxonRelationship
> result
= new HashSet
<>();
801 Query query
= getSession().createQuery(queryStr
);
802 @SuppressWarnings("unchecked")
803 List
<List
<Integer
>> partitionList
= splitIdList(query
.list(), DEFAULT_SET_SUBTREE_PARTITION_SIZE
);
804 for (List
<Integer
> relIdList
: partitionList
){
805 List
<TaxonRelationship
> relList
= taxonRelDao
.loadList(relIdList
, null, null);
806 for (TaxonRelationship rel
: relList
){
808 rel
= CdmBase
.deproxy(rel
);
809 if (newRef
== null && rel
.getCitation() !=null
810 || newRef
!= null && (rel
.getCitation() == null || !newRef
.equals(rel
.getCitation()) )){
811 rel
.setCitation(newRef
);
815 if (rel
.getCitationMicroReference() != null){
816 rel
.setCitationMicroReference(null);
820 //TODO do we also need to add NamedSource to result?
822 if (monitor
.isCanceled()){
827 commitAndRestartTransaction();
833 private List
<List
<Integer
>> splitIdList(List
<Integer
> idList
, Integer size
){
834 List
<List
<Integer
>> result
= new ArrayList
<>();
835 for (int i
= 0; (i
*size
)<idList
.size(); i
++) {
836 int upper
= Math
.min((i
+1)*size
, idList
.size());
837 result
.add(idList
.subList(i
*size
, upper
));
843 public int countPublishForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, boolean publish
, boolean includeSharedTaxa
, boolean includeHybrids
) {
844 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.COUNT
);
845 queryStr
+= " AND t.publish != :publish ";
846 System
.out
.println(queryStr
);
847 Query query
= getSession().createQuery(queryStr
);
848 query
.setBoolean("publish", publish
);
849 return ((Long
)query
.uniqueResult()).intValue();
853 public int countPublishForSubtreeSynonyms(TreeIndex subTreeIndex
, boolean publish
, boolean includeSharedTaxa
, boolean includeHybrids
) {
854 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.COUNT
);
855 queryStr
+= " AND syn.publish != :publish ";
856 Query query
= getSession().createQuery(queryStr
);
857 query
.setBoolean("publish", publish
);
858 return ((Long
)query
.uniqueResult()).intValue();
862 public Set
<TaxonBase
> setPublishForSubtreeAcceptedTaxa(TreeIndex subTreeIndex
, boolean publish
,
863 boolean includeSharedTaxa
, boolean includeHybrids
, IProgressMonitor monitor
) {
864 String queryStr
= forSubtreeAcceptedQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.ID
);
865 queryStr
+= " AND t.publish != :publish ";
866 return setPublish(publish
, queryStr
, null, monitor
);
870 public Set
<TaxonBase
> setPublishForSubtreeSynonyms(TreeIndex subTreeIndex
, boolean publish
,
871 boolean includeSharedTaxa
, boolean includeHybrids
, IProgressMonitor monitor
) {
872 String queryStr
= forSubtreeSynonymQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.ID
);
873 queryStr
+= " AND syn.publish != :publish ";
874 return setPublish(publish
, queryStr
, null, monitor
);
878 public int countPublishForSubtreeRelatedTaxa(TreeIndex subTreeIndex
, boolean publish
, boolean includeSharedTaxa
, boolean includeHybrids
) {
879 String queryStr
= forSubtreeRelatedTaxaQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.COUNT
);
880 queryStr
+= " AND relTax.publish != :publish ";
881 Query query
= getSession().createQuery(queryStr
);
882 query
.setBoolean("publish", publish
);
883 return ((Long
)query
.uniqueResult()).intValue();
887 public Set
<TaxonBase
> setPublishForSubtreeRelatedTaxa(TreeIndex subTreeIndex
, boolean publish
,
888 Set
<UUID
> relationTypes
, boolean includeSharedTaxa
, boolean includeHybrids
,
889 IProgressMonitor monitor
) {
890 String queryStr
= forSubtreeRelatedTaxaQueryStr(includeSharedTaxa
, subTreeIndex
, !includeHybrids
, SelectMode
.ID
);
891 queryStr
+= " AND relTax.publish != :publish ";
892 queryStr
+= " AND rel.type.uuid IN (:relTypeUuid)";
893 return setPublish(publish
, queryStr
, relationTypes
, monitor
);
896 private <T
extends TaxonBase
<?
>> Set
<T
> setPublish(boolean publish
, String queryStr
, Set
<UUID
> relTypeUuids
, IProgressMonitor monitor
) {
897 Set
<T
> result
= new HashSet
<>();
898 Query query
= getSession().createQuery(queryStr
);
899 query
.setBoolean("publish", publish
);
900 if (relTypeUuids
!= null && !relTypeUuids
.isEmpty()){
901 query
.setParameterList("relTypeUuid", relTypeUuids
);
903 @SuppressWarnings("unchecked")
904 List
<List
<Integer
>> partitionList
= splitIdList(query
.list(), DEFAULT_SET_SUBTREE_PARTITION_SIZE
);
905 for (List
<Integer
> taxonIdList
: partitionList
){
906 List
<TaxonBase
> taxonList
= taxonDao
.loadList(taxonIdList
, null, null);
907 for (TaxonBase
<?
> taxonBase
: taxonList
){
908 if (taxonBase
!= null){
909 if (taxonBase
.isPublish() != publish
){ //to be on the save side
910 taxonBase
.setPublish(publish
);
911 result
.add((T
)CdmBase
.deproxy(taxonBase
));
914 if (monitor
.isCanceled()){
919 commitAndRestartTransaction();
924 private String
forSubtreeSynonymQueryStr(boolean includeSharedTaxa
, TreeIndex subTreeIndex
, boolean excludeHybrids
, SelectMode mode
) {
925 String queryStr
= "SELECT " + mode
.hql("syn")
926 + " FROM TaxonNode tn "
927 + " JOIN tn.taxon t "
928 + " JOIN t.synonyms syn "
929 + " LEFT JOIN syn.name n "
930 + " LEFT JOIN syn.secSource ss ";
931 String whereStr
= " tn.treeIndex LIKE '%1$s%%' ";
932 if (!includeSharedTaxa
){
933 whereStr
+= " AND NOT EXISTS ("
934 + "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
936 whereStr
= handleExcludeHybrids(whereStr
, excludeHybrids
, "syn");
937 queryStr
+= " WHERE " + String
.format(whereStr
, subTreeIndex
.toString());
942 private String
handleExcludeHybrids(String whereStr
, boolean excludeHybrids
, String t
) {
945 String hybridWhere
= " AND (n is NULL OR "
946 + " (n.monomHybrid=0 AND n.binomHybrid=0 "
947 + " AND n.trinomHybrid=0 AND n.hybridFormula=0 )) ";
949 whereStr
+= hybridWhere
; //String.format(hybridWhere, t);
954 private String
forSubtreeRelatedTaxaQueryStr(boolean includeSharedTaxa
, TreeIndex subTreeIndex
,
955 boolean excludeHybrids
, SelectMode mode
) {
956 String queryStr
= "SELECT " + mode
.hql("relTax")
957 + " FROM TaxonNode tn "
958 + " JOIN tn.taxon t "
959 + " JOIN t.relationsToThisTaxon rel"
960 + " JOIN rel.relatedFrom relTax "
961 + " LEFT JOIN relTax.name n ";
962 String whereStr
=" tn.treeIndex LIKE '%1$s%%' ";
963 if (!includeSharedTaxa
){
964 //toTaxon should only be used in the given subtree
965 whereStr
+= " AND NOT EXISTS ("
966 + "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
967 //from taxon should not be used in another classification
968 whereStr
+= " AND NOT EXISTS ("
969 + "FROM TaxonNode tn3 WHERE tn3.taxon = relTax AND tn3.treeIndex not like '%1$s%%') ";
970 //fromTaxon should not be related as e.g. pro parte synonym or misapplication to
971 //another taxon which is not part of the subtree
972 //TODO and has not the publish state
973 whereStr
+= " AND NOT EXISTS ("
974 + "FROM TaxonNode tn4 JOIN tn4.taxon t2 JOIN t2.relationsToThisTaxon rel2 "
975 + " WHERE rel2.relatedFrom = relTax AND tn4.treeIndex not like '%1$s%%' "
976 + " AND tn4.taxon.publish != :publish ) ";
978 whereStr
= handleExcludeHybrids(whereStr
, excludeHybrids
, "relTax");
979 queryStr
+= " WHERE " + String
.format(whereStr
, subTreeIndex
.toString());
987 private String
forSubtreeRelationQueryStr(boolean includeSharedTaxa
, boolean overwriteExisting
,
988 TreeIndex subTreeIndex
, SelectMode mode
) {
990 String queryStr
= "SELECT " + mode
.hql("rel")
991 + " FROM TaxonNode tn "
992 + " JOIN tn.taxon t "
993 + " JOIN t.relationsToThisTaxon rel "
994 + " LEFT JOIN rel.source src ";
995 String whereStr
=" tn.treeIndex LIKE '%1$s%%' ";
996 if (!includeSharedTaxa
){
997 //toTaxon should only be used in the given subtree
998 whereStr
+= " AND NOT EXISTS ("
999 + "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
1001 queryStr
+= " WHERE " + String
.format(whereStr
, subTreeIndex
.toString());
1002 if (!overwriteExisting
){
1003 queryStr
+= " AND (rel.source IS NULL OR src.citation IS NULL) ";
1009 private enum SelectMode
{
1010 COUNT(" count(*) "),
1015 SelectMode(String hql
){
1018 public String
hql(String prefix
){
1022 return CdmUtils
.Nz(prefix
)+"." + hql
;
1024 return CdmUtils
.Nz(prefix
) + hql
;
1026 default: return hql
;
1032 private String
forSubtreeAcceptedQueryStr(boolean includeSharedTaxa
, TreeIndex subTreeIndex
, boolean excludeHybrids
, SelectMode mode
) {
1033 String queryStr
= "SELECT " + mode
.hql("t")
1034 + " FROM TaxonNode tn "
1035 + " JOIN tn.taxon t "
1036 + " LEFT JOIN t.name n "
1037 + " LEFT JOIN t.secSource ss ";
1038 String whereStr
= " tn.treeIndex like '%1$s%%' ";
1039 if (!includeSharedTaxa
){
1040 whereStr
+= " AND NOT EXISTS ("
1041 + "FROM TaxonNode tn2 WHERE tn2.taxon = t AND tn2.treeIndex not like '%1$s%%') ";
1043 whereStr
= handleExcludeHybrids(whereStr
, excludeHybrids
, "t");
1044 queryStr
+= " WHERE " + String
.format(whereStr
, subTreeIndex
.toString());
1050 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(Classification classification
, Integer limit
, String pattern
, boolean searchForClassifications
, boolean includeDoubtful
) {
1052 Query query
= createQueryForUuidAndTitleCache(limit
, classification
.getUuid(), pattern
, includeDoubtful
);
1053 @SuppressWarnings("unchecked")
1054 List
<SortableTaxonNodeQueryResult
> result
= query
.list();
1057 if (searchForClassifications
){
1058 String queryString
= "SELECT new " + SortableTaxonNodeQueryResult
.class.getName() + "("
1059 + " node.uuid, node.id, node.classification.titleCache"
1061 + " FROM TaxonNode AS node "
1062 + " WHERE node.classification.id = " + classification
.getId() +
1063 " AND node.taxon IS NULL";
1064 if (pattern
!= null){
1065 if (pattern
.equals("?")){
1068 if (!pattern
.endsWith("*")){
1071 pattern
= pattern
.replace("*", "%");
1072 pattern
= pattern
.replace("?", "%");
1073 queryString
= queryString
+ " AND node.classification.titleCache LIKE (:pattern) " ;
1076 query
= getSession().createQuery(queryString
);
1079 query
.setMaxResults(limit
);
1082 if (pattern
!= null && !pattern
.equals("?")){
1083 query
.setParameter("pattern", pattern
);
1085 @SuppressWarnings("unchecked")
1086 List
<SortableTaxonNodeQueryResult
> resultClassifications
= query
.list();
1088 result
.addAll(resultClassifications
);
1091 if(result
.size() == 0){
1094 List
<UuidAndTitleCache
<TaxonNode
>> list
= new ArrayList
<>(result
.size());
1095 Collections
.sort(result
, new SortableTaxonNodeQueryResultComparator());
1096 for (SortableTaxonNodeQueryResult resultDTO
: result
){
1097 list
.add(new UuidAndTitleCache
<>(TaxonNode
.class, resultDTO
.getTaxonNodeUuid(), resultDTO
.getTaxonNodeId(), resultDTO
.getTaxonTitleCache()));
1105 public List
<TaxonNodeDto
> getTaxonNodeDto(Integer limit
, String pattern
, UUID classificationUuid
) {
1106 String queryString
= "SELECT new " + SortableTaxonNodeQueryResult
.class.getName() + "("
1107 + "tn.uuid, tn.id, t.titleCache, rank "
1109 + " FROM TaxonNode tn "
1110 + " INNER JOIN tn.taxon AS t "
1111 + " INNER JOIN tn.classification AS cls "
1112 + " INNER JOIN t.name AS name "
1113 + " LEFT OUTER JOIN name.rank AS rank "
1114 + " WHERE t.titleCache LIKE :pattern ";
1115 if(classificationUuid
!= null){
1116 queryString
+= "AND cls.uuid = :classificationUuid";
1119 Query query
= getSession().createQuery(queryString
);
1121 query
.setParameter("pattern", pattern
.toLowerCase()+"%");
1122 if(classificationUuid
!= null){
1123 query
.setParameter("classificationUuid", classificationUuid
);
1126 @SuppressWarnings("unchecked")
1127 List
<SortableTaxonNodeQueryResult
> result
= query
.list();
1128 Collections
.sort(result
, new SortableTaxonNodeQueryResultComparator());
1130 List
<TaxonNodeDto
> list
= new ArrayList
<>();
1131 for(SortableTaxonNodeQueryResult queryDTO
: result
){
1132 list
.add(new TaxonNodeDto(queryDTO
.getTaxonNodeUuid(), queryDTO
.getTaxonNodeId(), queryDTO
.getTaxonTitleCache()));
1138 public List
<TaxonNodeDto
> getUuidAndTitleCache(Integer limit
, String pattern
, UUID classificationUuid
) {
1139 return getUuidAndTitleCache(limit
, pattern
, classificationUuid
, false);
1143 public List
<UuidAndTitleCache
<TaxonNode
>> getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(
1144 Classification classification
, Integer limit
, String pattern
, boolean searchForClassifications
) {
1145 return getTaxonNodeUuidAndTitleCacheOfAcceptedTaxaByClassification(classification
, limit
, pattern
, searchForClassifications
, false);