jenkins merging release branch into master (strategy: theirs)
[cdm-vaadin.git] / src / main / java / eu / etaxonomy / cdm / service / CdmFilterablePagingProvider.java
1 /**
2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.cdm.service;
10
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.List;
14
15 import org.apache.logging.log4j.LogManager;
16 import org.apache.logging.log4j.Logger;
17 import org.hibernate.Session;
18 import org.hibernate.criterion.Criterion;
19 import org.vaadin.viritin.fields.LazyComboBox.FilterableCountProvider;
20 import org.vaadin.viritin.fields.LazyComboBox.FilterablePagingProvider;
21
22 import eu.etaxonomy.cdm.api.service.IIdentifiableEntityService;
23 import eu.etaxonomy.cdm.api.service.pager.Pager;
24 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
25 import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
26 import eu.etaxonomy.cdm.persistence.query.MatchMode;
27 import eu.etaxonomy.cdm.persistence.query.OrderHint;
28
29 /**
30 * @author a.kohlbecker
31 * @since Jun 7, 2017
32 */
33 public class CdmFilterablePagingProvider<T extends IdentifiableEntity, V extends T>
34 implements FilterablePagingProvider<V>, FilterableCountProvider {
35
36 private static final Logger logger = LogManager.getLogger();
37
38 private static final List<String> DEFAULT_INIT_STRATEGY = Arrays.asList("$");
39
40 public static final String QUERY_STRING_PLACEHOLDER = "{query-string}";
41
42 private int pageSize = 20;
43
44 private IIdentifiableEntityService<T> service;
45
46 private Class<V> type = null;
47
48 private MatchMode matchMode = MatchMode.ANYWHERE;
49
50 private List<OrderHint> orderHints = OrderHint.ORDER_BY_TITLE_CACHE.asList();
51
52 private List<String> initStrategy = DEFAULT_INIT_STRATEGY;
53
54 private List<Criterion> criteria = new ArrayList<>();
55
56 private List<Restriction<?>> restrictions = new ArrayList<>();
57
58 protected MatchMode getMatchMode() {
59 return matchMode;
60 }
61 protected void setMatchMode(MatchMode matchMode) {
62 this.matchMode = matchMode;
63 }
64
65 protected List<OrderHint> getOrderHints() {
66 return orderHints;
67 }
68 protected void setOrderHints(List<OrderHint> orderHints) {
69 this.orderHints = orderHints;
70 }
71
72 /**
73 * With defaults for matchMode = MatchMode.ANYWHERE and orderHints = OrderHint.ORDER_BY_TITLE_CACHE
74 */
75 public CdmFilterablePagingProvider(IIdentifiableEntityService<T> service) {
76 this(service, null);
77 }
78
79 /**
80 * With defaults for matchMode = MatchMode.ANYWHERE and orderHints = OrderHint.ORDER_BY_TITLE_CACHE
81 */
82 public CdmFilterablePagingProvider(IIdentifiableEntityService<T> service, Class<V> type) {
83 super();
84 this.type = type;
85 this.service = service;
86
87 // LogUtils.setLevel("org.hibernate.SQL", Level.TRACE);
88 }
89
90 public CdmFilterablePagingProvider(IIdentifiableEntityService<T> service, MatchMode matchMode, List<OrderHint> orderHints) {
91 this(service, null, matchMode, orderHints);
92 }
93
94 public <S extends T> CdmFilterablePagingProvider(IIdentifiableEntityService<T> service, Class<V> type, MatchMode matchMode, List<OrderHint> orderHints) {
95 this.type = type;
96 this.service = service;
97 this.matchMode = matchMode;
98 this.orderHints = orderHints;
99
100 // LogUtils.setLevel("org.hibernate.SQL", Level.TRACE);
101 }
102
103 @Override
104 public List<V> findEntities(int firstRow, String filter) {
105
106 checkNotMixed();
107
108 Integer pageIndex = firstRow / pageSize;
109 Pager<V> page;
110 clearSession(); // clear the session from remains of previous service calls, see issue #7559
111 if(!restrictions.isEmpty()){
112 List<Restriction<?>> preparedRestrictions = prepareRestrictions(filter, matchMode);
113 page = service.findByTitleWithRestrictions(
114 type,
115 filter,
116 matchMode,
117 preparedRestrictions,
118 pageSize,
119 pageIndex ,
120 orderHints,
121 initStrategy
122 );
123 } else {
124 page = service.findByTitle(
125 type,
126 filter,
127 matchMode,
128 criteria,
129 pageSize,
130 pageIndex ,
131 orderHints,
132 initStrategy
133 );
134 }
135
136 if(logger.isTraceEnabled()){
137 logger.trace("findEntities() - page: " + page.getCurrentIndex() + "/" + page.getPagesAvailable() + " totalRecords: " + page.getCount() + "\n" + page.getRecords());
138 }
139
140 // LogUtils.setLevel("org.hibernate.SQL", Level.TRACE);
141 return page.getRecords();
142 }
143
144 @Override
145 public int size(String filter) {
146
147 checkNotMixed();
148
149 clearSession(); // clear the session from remains of previous service calls, see issue #7559
150 long count = 0;
151 if(!restrictions.isEmpty()){
152 // LogUtils.setLevel("org.hibernate.SQL", Level.TRACE);
153 List<Restriction<?>> preparedRestrictions = prepareRestrictions(filter, matchMode);
154 count = service.countByTitleWithRestrictions(type, filter, matchMode, preparedRestrictions);
155 } else {
156 count = service.countByTitle(type, filter, matchMode, criteria);
157 }
158
159
160 if(logger.isTraceEnabled()){
161 logger.trace("size() - count: " + count);
162 }
163 return Long.valueOf(count).intValue();
164 }
165
166 /**
167 * see issue #7559
168 */
169 public void clearSession() {
170 Session session = service.getSession();
171 if(session.isOpen()){
172 session.clear();
173 }
174 }
175
176 private List<Restriction<?>> prepareRestrictions(String filter, MatchMode matchMode) {
177 List<Restriction<?>> prepared = new ArrayList<>(restrictions.size());
178 for(Restriction<?> r : restrictions) {
179 List<Object> values = new ArrayList<>(r.getValues().size());
180 for(Object v : r.getValues()){
181 if(v instanceof String){
182 String expandedValue = ((String)v).replace(QUERY_STRING_PLACEHOLDER, matchMode.queryStringFrom(filter));
183 values.add(expandedValue);
184 } else {
185 values.add(v);
186 }
187 }
188 prepared.add(new Restriction<>(r.getPropertyName(), r.getOperator(), r.getMatchMode(), values.toArray(new Object[values.size()])));
189 }
190 return prepared;
191 }
192
193 protected void checkNotMixed() {
194 if(!restrictions.isEmpty() && !criteria.isEmpty()){
195 throw new RuntimeException("Citeria and Restrictions must not be used at the same time");
196 }
197 }
198
199 public int getPageSize() {
200 return pageSize;
201 }
202
203 public void setPageSize(int pageSize) {
204 this.pageSize = pageSize;
205 }
206
207 public List<String> getInitStrategy() {
208 return initStrategy;
209 }
210
211 public void setInitStrategy(List<String> initStrategy) {
212 this.initStrategy = initStrategy;
213 }
214
215 /**
216 * The list of criteria is initially empty.
217 *
218 * @return the criteria
219 */
220 public List<Criterion> getCriteria() {
221 return criteria;
222 }
223
224 public void addCriterion(Criterion criterion){
225 criteria.add(criterion);
226 }
227
228 /**
229 * The list of restrictions is initially empty.
230 * <p>
231 * Occurrences of the {@link QUERY_STRING_PLACEHOLDER} in the value
232 * of String type Restrictions will be replaced by the <code>filter</code> parameter passed to the paging provider.
233 *
234 *
235 * @return the restrictions
236 */
237 public List<Restriction<?>> getRestrictions() {
238 return restrictions;
239 }
240
241 /**
242 * Occurrences of the {@link QUERY_STRING_PLACEHOLDER} in the value
243 * of String type Restrictions will be replaced by the <code>filter</code> parameter passed to the paging provider.
244 *
245 * @param restriction
246 */
247 public void addRestriction(Restriction<?> restriction){
248 restrictions.add(restriction);
249 }
250 }