View Javadoc

1   /*
2    * Copyright 2007-2008 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.core.jpa.criteria;
17  
18  import java.math.BigDecimal;
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.util.LinkedHashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import javax.persistence.Query;
27  
28  import org.kuali.rice.core.jpa.criteria.QueryByCriteria.QueryByCriteriaType;
29  
30  /**
31   * A criteria builder for JPQL Query objects.
32   * 
33   * TODO: Rewrite this class with a better criteria building algorithm.
34   * 
35   * @author Kuali Rice Team (rice.collab@kuali.org)
36   */
37  @SuppressWarnings("unchecked")
38  public class Criteria {
39  
40  	private Integer searchLimit;
41  	
42  	private String entityName;
43  
44  	private String alias;
45  	
46  	private int bindParamCount;
47  	
48  	private boolean distinct = false;
49  
50  	protected List tokens = new ArrayList();
51  
52  	private List orderByTokens = new ArrayList();
53  
54  	protected Map<String, Object> params = new LinkedHashMap<String, Object>();
55  
56  	public Criteria(String entityName) {
57  		this(entityName, "a");
58  	}
59  
60  	public Criteria(String entityName, String alias) {
61  		this.entityName = entityName;
62  		this.alias = alias;
63  	}
64  
65  	public void between(String attribute, Object value1, Object value2) {
66  		String fixedAttr = fixAttr(attribute);
67  		tokens.add(" (" + alias + "." + attribute + " BETWEEN :" + fixedAttr + "-b1 AND :" + fixedAttr + "-b2) ");
68  		params.put(fixedAttr+ "-b1", value1);
69  		params.put(fixedAttr+ "-b2", value2);
70  	}
71  	
72  	/**
73  	 * This method ...
74  	 * 
75  	 * @param string
76  	 * @return
77  	 */
78  	private String fixAttr(String string) {
79  		return string.replace(".", "_");
80  	}
81  
82  	public void eq(String attribute, Object value) {
83  		String fixedAttr = fixAttr(attribute);
84  		tokens.add(alias + "." + attribute + " = :" + fixedAttr + " ");
85  		params.put(fixedAttr, value);
86  	}
87  
88  	public void gt(String attribute, Object value) {
89  		String fixedAttr = fixAttr(attribute);
90  		tokens.add(alias + "." + attribute + " > :" + fixedAttr + " ");
91  		params.put(fixedAttr, value);
92  	}
93  
94  	public void gte(String attribute, Object value) {
95  		String fixedAttr = fixAttr(attribute);
96  		tokens.add(alias + "." + attribute + " >= :" + fixedAttr + " ");
97  		params.put(fixedAttr, value);
98  	}
99  
100 	public void like(String attribute, Object value) {
101 		String fixedAttr = fixAttr(attribute);
102 		if (attribute.contains("__JPA_ALIAS__")) {
103 			String bind = "BIND_PARAM_" + (++bindParamCount);
104 			tokens.add(attribute + " LIKE :" + bind + " ");
105 			params.put(bind, value);
106 		} else {
107 			tokens.add(alias + "." + attribute + " LIKE :" + stripFunctions(fixedAttr) + " ");
108 			params.put(fixedAttr, value);
109 		}
110 	}
111 
112 	public void notLike(String attribute, Object value) {
113 		String fixedAttr = fixAttr(attribute);
114 		tokens.add(alias + "." + attribute + " NOT LIKE :" + fixedAttr + " ");
115 		params.put(fixedAttr, value);
116 	}
117 
118 	public void lt(String attribute, Object value) {
119 		String fixedAttr = fixAttr(attribute);
120 		tokens.add(alias + "." + attribute + " < :" + fixedAttr + " ");
121 		params.put(fixedAttr, value);
122 	}
123 
124 	public void lte(String attribute, Object value) {
125 		String fixedAttr = fixAttr(attribute);
126 		tokens.add(alias + "." + attribute + " <= :" + fixedAttr + " ");
127 		params.put(fixedAttr, value);
128 	}
129 
130 	public void ne(String attribute, Object value) {
131 		String fixedAttr = fixAttr(attribute);
132 		tokens.add(alias + "." + attribute + " != :" + fixedAttr + " ");
133 		params.put(fixedAttr, value);
134 	}
135 
136 	public void isNull(String attribute) {
137 		tokens.add(alias + "." + attribute + " IS NULL ");
138 	}
139 
140 	public void rawJpql(String jpql) {
141 		tokens.add(" " + jpql + " ");
142 	}
143 
144 	public void in(String attribute, List values) {
145 		String in = "";
146 		for (Object object : values) {
147 			in += "'"+object + "',";
148 		}
149 		if (!"".equals(in)) {
150 			in = in.substring(0, in.length()-1);
151 		}
152 		tokens.add(alias + "." + attribute + " IN (" + in + ") ");
153 	}
154 
155 	public void notIn(String attribute, List values) {
156 		String in = "";
157 		for (Object object : values) {
158 			in += "'"+object + "',";
159 		}
160 		if (!"".equals(in)) {
161 			in = in.substring(in.length()-1);
162 		}
163 		tokens.add(alias + "." + attribute + " NOT IN (" + in + ") ");
164 	}
165 
166 	public void orderBy(String attribute, boolean sortAscending) {
167 		String sort = (sortAscending ? "ASC" : "DESC");
168 		orderByTokens.add(alias + "." + attribute + " " + sort + " ");
169 	}
170 
171 	public void and(Criteria and) {
172 		tokens.add(new AndCriteria(and));
173 	}
174 
175 	public void or(Criteria or) {
176 		tokens.add(new OrCriteria(or));
177 	}
178 
179 	public String toQuery(QueryByCriteriaType type) {
180 		String queryType = type.toString();
181 		if (type.equals(QueryByCriteriaType.SELECT)) {
182 			if(distinct){
183 				queryType += " " + "DISTINCT";
184 			}
185 			
186 			queryType += " " + alias;
187 		}
188 		String queryString = queryType + " FROM " + entityName + " AS " + alias;
189 		if (!tokens.isEmpty()) {
190 			queryString += " WHERE " + buildWhere();
191 		}
192 		if (!orderByTokens.isEmpty()) {
193 			queryString += " ORDER BY ";
194 			int count = 0;
195 			for (Iterator iterator = orderByTokens.iterator(); iterator.hasNext();) {
196 				Object token = (Object) iterator.next();
197 				if (count == 0) {
198 					count++;
199 				} else {
200 					queryString += ", ";
201 				}
202 				queryString += (String) token;
203 			}
204 		}		
205 		return fix(queryString);
206 	}
207 	
208 	public String toCountQuery() {
209 		String queryString = "SELECT COUNT(*) FROM " + entityName + " AS " + alias;
210 		if (!tokens.isEmpty()) {
211 			queryString += " WHERE " + buildWhere();
212 		}
213 		return fix(queryString);
214 	}
215 
216 	private String fix(String queryString) {
217 		queryString = queryString.replaceAll("__JPA_ALIAS__", alias);
218 		return queryString;
219 	}
220 	
221 	private String buildWhere() {
222 		String queryString = "";
223 		int i = 0;
224 		for (Iterator iterator = tokens.iterator(); iterator.hasNext();) {
225 			Object token = (Object) iterator.next();
226 			if (token instanceof Criteria) {
227 				String logic = "";
228 				if (i>0 && token instanceof AndCriteria) {
229 					logic = " AND ";
230 				} else if (i>0 && token instanceof OrCriteria) {
231 					logic = " OR ";
232 				}
233 			queryString += logic + " (" + ((Criteria) token).buildWhere() + ") ";
234 			} else {
235 				if(i>0){
236 					queryString += " AND " + (String) token;
237 				}else{
238 					queryString += (String) token;
239 				}
240 			}
241 			i++;
242 		}
243 		return queryString;
244 	}
245 
246 	// Keep this package access so the QueryByCriteria can call it from this package.
247 	void prepareParameters(Query query) {
248 		prepareParameters(query, tokens, params);
249 	}
250 	
251 	void prepareParameters(Query query, List tokens, Map<String, Object> params) {
252 		for (Map.Entry<String, Object> param : params.entrySet()) {
253 			Object value = param.getValue();
254 			if (value instanceof BigDecimal) {
255 				value = new Long(((BigDecimal)value).longValue());
256 			}
257 			if (value instanceof String) {
258 				value = ((String)value).replaceAll("\\*", "%");
259 			}
260 			query.setParameter(param.getKey(), value);
261 		}
262 		for (Iterator iterator = tokens.iterator(); iterator.hasNext();) {
263 			Object token = (Object) iterator.next();
264 			if (token instanceof Criteria) {
265 				prepareParameters(query, ((Criteria)token).tokens, ((Criteria)token).params);
266 			}
267 		}
268 	}
269 	
270 	private class AndCriteria extends Criteria {
271 		public AndCriteria(Criteria and) {
272 			super(and.entityName, and.alias);
273 			this.tokens = new ArrayList(and.tokens);
274 			this.params = new HashMap(and.params);
275 		}		
276 	}
277 
278 	private class OrCriteria extends Criteria {
279 		public OrCriteria(Criteria or) {
280 			super(or.entityName, or.alias);
281 			this.tokens = new ArrayList(or.tokens);
282 			this.params = new HashMap(or.params);
283 		}		
284 	}
285 	
286 	public Integer getSearchLimit() {
287 		return this.searchLimit;
288 	}
289 
290 	public void setSearchLimit(Integer searchLimit) {
291 		this.searchLimit = searchLimit;
292 	}
293 
294 
295 	public void notNull(String attribute) {
296 		tokens.add(alias + "." + attribute + " IS NOT NULL ");
297 	}
298 
299 	public void distinct(boolean distinct){
300 		this.distinct = distinct;
301 	}
302 	
303 	/**
304 	 * This method ...
305 	 * 
306 	 * @param string
307 	 * @param timestamp
308 	 * @param timestamp2
309 	 */
310 	public void notBetween(String attribute, Object value1,
311 			Object value2) {
312 		String fixedAttr = fixAttr(attribute);
313 		tokens.add(" (" + alias + "." + attribute + " NOT BETWEEN :" + fixedAttr + "-b1 AND :" + fixedAttr + "-b2) ");
314 		params.put(fixedAttr + "-b1", value1);
315 		params.put(fixedAttr + "-b2", value2);
316 	}
317 
318 	/**
319 	 * This method ...
320 	 * 
321 	 * @param string
322 	 * @param responsibilitySubQuery
323 	 */
324 	public void in(String match, Criteria subQuery, String attribute) {
325 		if("a".equals(subQuery.alias)){
326 			subQuery.alias="b";
327 		}
328 		String whereClause = "";
329 		if(subQuery.tokens.isEmpty()){
330 			whereClause = "WHERE ";
331 		}else{
332 			whereClause = "AND ";
333 		}
334 		whereClause += subQuery.alias+"."+attribute + " = " + alias+"."+match;
335 		
336 		tokens.add("EXISTS (" + subQuery.toQuery(QueryByCriteriaType.SELECT) + whereClause + " ) ");
337 		
338 	}
339 	
340 	private String stripFunctions(String attribute) {
341 	    int index = attribute.lastIndexOf('(');
342 	    if(index != -1) {
343 	        return attribute.substring(index+1, attribute.indexOf(')'));
344 	    }
345 	    
346 	    return attribute;
347 	}
348 
349 	public String getAlias() {
350 		return this.alias;
351 	}
352 	
353 }