Clover Coverage Report - KS Common 1.3.0-SNAPSHOT (Aggregated)
Coverage timestamp: Thu Apr 28 2011 06:00:36 EDT
../../../../../../img/srcFileCovDistChart0.png 0% of files have more coverage
127   289   48   63.5
60   213   0.38   2
2     24  
1    
 
  AbstractSearchableCrudDaoImpl       Line # 40 127 0% 48 189 0% 0.0
 
No Tests
 
1    /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10    * software distributed under the License is distributed on an "AS IS"
11    * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12    * or implied. See the License for the specific language governing
13    * permissions and limitations under the License.
14    */
15   
16    package org.kuali.student.common.dao.impl;
17   
18    import java.text.ParseException;
19    import java.text.SimpleDateFormat;
20    import java.util.ArrayList;
21    import java.util.Collection;
22    import java.util.List;
23    import java.util.Map;
24    import java.util.StringTokenizer;
25   
26    import javax.persistence.Query;
27   
28    import org.apache.log4j.Logger;
29    import org.kuali.student.common.dao.SearchableDao;
30    import org.kuali.student.common.search.dto.QueryParamInfo;
31    import org.kuali.student.common.search.dto.ResultColumnInfo;
32    import org.kuali.student.common.search.dto.SearchParam;
33    import org.kuali.student.common.search.dto.SearchRequest;
34    import org.kuali.student.common.search.dto.SearchResult;
35    import org.kuali.student.common.search.dto.SearchResultCell;
36    import org.kuali.student.common.search.dto.SearchResultRow;
37    import org.kuali.student.common.search.dto.SearchTypeInfo;
38    import org.kuali.student.common.search.dto.SortDirection;
39   
 
40    public class AbstractSearchableCrudDaoImpl extends AbstractCrudDaoImpl
41    implements SearchableDao {
42    final Logger LOG = Logger.getLogger(AbstractSearchableCrudDaoImpl.class);
43    private static SimpleDateFormat df = new SimpleDateFormat("EEE MMM dd hh:mm:ss zzz yyyy");
44   
 
45  0 toggle @Override
46    public SearchResult search(SearchRequest searchRequest, Map<String, String> queryMap, SearchTypeInfo searchTypeInfo) {
47  0 String searchKey = searchRequest.getSearchKey();
48   
49  0 boolean isNative = false;
50   
51    //retrieve the SELECT statement from search type definition
52  0 String queryString = queryMap.get(searchKey);
53  0 String optionalQueryString = "";
54  0 if(null == queryString){
55  0 LOG.error("No SQL query was found for searchKey:"+searchKey);
56    }
57   
58  0 if(queryString.toUpperCase().startsWith("NATIVE:")){
59  0 queryString = queryString.substring("NATIVE:".length());
60  0 isNative = true;
61    }
62   
63    //add in optional
64  0 List<SearchParam> searchParamsTemp = new ArrayList<SearchParam>(searchRequest.getParams());
65    // internalQueryParms is used only internally to know which parameters have to be set in the query
66  0 List<SearchParam> internalQueryParms = new ArrayList<SearchParam>(searchRequest.getParams());
67  0 for(SearchParam searchParam : searchParamsTemp){
68  0 for(QueryParamInfo queryParam:searchTypeInfo.getSearchCriteriaTypeInfo().getQueryParams()){
69    // check to see if optional param has any values set.
70  0 if(queryParam.isOptional()&&queryParam.getKey().equals(searchParam.getKey())&&searchParam.getValue()!=null){
71  0 if(!optionalQueryString.isEmpty()){
72  0 optionalQueryString += " AND ";
73    }
74   
75    //if optional query parameter has only a column name then create proper search expression
76  0 String condition = queryMap.get(searchParam.getKey());
77  0 if (condition.trim().startsWith("!!")) {
78  0 String substitutionType = condition.trim().substring("!!".length());
79    // to detect queryMap value in the form of !!____ ___
80  0 if (condition.contains(" ")) {
81  0 substitutionType = condition.substring("!!".length(), condition.indexOf(" "));
82    }
83   
84  0 if (substitutionType != null && substitutionType.equals("NUMBER_RANGE")) {
85  0 String realCondition = condition.substring("!!".length() + substitutionType.length()).trim();
86  0 String queryValue = (String)searchParam.getValue();
87    // if the query value is of the form n1 - n2
88  0 if (queryValue != null && queryValue.trim().contains("-")) {
89  0 StringTokenizer strTokenizer = new StringTokenizer(queryValue.trim(),"-");
90  0 if (strTokenizer.hasMoreElements()) {
91  0 String strNum1 = strTokenizer.nextToken().trim();
92  0 String strNum2 = strTokenizer.nextToken().trim();
93  0 optionalQueryString +=
94    realCondition +
95    " BETWEEN " + "'" + strNum1 + "'" + " AND " + "'" + strNum2 + "'";
96  0 internalQueryParms.remove(searchParam);
97    }
98    } else {
99    // the value is just one number
100  0 optionalQueryString += realCondition + " = '" + queryValue + "'";
101  0 internalQueryParms.remove(searchParam);
102    }
103    }
104  0 } else if (condition.trim().contains(":")) {
105    //this parameter is not entered by end user but rather it is set with a default context value
106  0 String dataType = queryParam.getFieldDescriptor().getDataType();
107  0 if ((dataType != null) && "boolean".equals(dataType)) {
108  0 optionalQueryString += queryMap.get(searchParam.getKey()).replace(":" + searchParam.getKey().replace(".", "_"), searchParam.getValue().toString());
109  0 internalQueryParms.remove(searchParam);
110    } else {
111  0 optionalQueryString += queryMap.get(searchParam.getKey());
112    }
113    } else {
114    //comparison should be case insensitive and should include wild card such that we match beginning of a text
115    //and each word within text
116    //FIXME SQL injection can occur here - or NOT if we need to assemble SQL to cover various ways one can compare criteria to a text
117  0 optionalQueryString +=
118    "(LOWER(" + queryMap.get(searchParam.getKey()) + ") LIKE LOWER('" + searchParam.getValue() + "') || '%' OR " +
119    "LOWER(" + queryMap.get(searchParam.getKey()) + ") LIKE '% ' || LOWER('" + searchParam.getValue() + "') || '%')";
120  0 internalQueryParms.remove(searchParam);
121    }
122    }
123    }
124    }
125   
126    //Add in the where clause or And clause if needed for the optional criteria
127  0 if(!optionalQueryString.isEmpty()){
128  0 if(!queryString.toUpperCase().contains(" WHERE ")){
129  0 queryString += " WHERE ";
130    }
131    else {
132  0 queryString += " AND ";
133    }
134    }
135   
136    //Do ordering
137  0 String orderByClause = "";
138  0 if(!queryString.toUpperCase().contains("ORDER BY")&&searchRequest.getSortColumn()!=null){
139    //make sure the sort column is a real result column
140  0 int i = 0;
141   
142    //Get an array of the jpql results
143  0 int selectIndex = queryString.toLowerCase().indexOf("select")+"select".length();
144  0 int fromIndex = queryString.toLowerCase().indexOf(" from ");
145   
146  0 if (selectIndex >= 0 && fromIndex > selectIndex){
147  0 String[] jpqlResultColumns = queryString.substring(selectIndex, fromIndex).replaceAll("\\s", "").split(",");
148  0 for(ResultColumnInfo results : searchTypeInfo.getSearchResultTypeInfo().getResultColumns()){
149  0 if(results.getKey().equals(searchRequest.getSortColumn())){
150  0 orderByClause = " ORDER BY "+jpqlResultColumns[i]+" ";
151  0 if(searchRequest.getSortDirection()!=null && searchRequest.getSortDirection()==SortDirection.DESC){
152  0 orderByClause += "DESC ";
153    }else{
154  0 orderByClause += "ASC ";
155    }
156    }
157  0 i++;
158    }
159    }
160    }
161   
162    //Create the query
163  0 String finalQueryString = queryString + optionalQueryString + orderByClause;
164   
165    //remove special characters and extra spaces
166    //finalQueryString = finalQueryString.replaceAll("[\n\r\t]", " ");
167    //finalQueryString = finalQueryString.replaceAll("\\s+", " ");
168   
169  0 Query query;
170  0 if(isNative){
171  0 LOG.info("Native Query:"+finalQueryString);
172  0 query = em.createNativeQuery(finalQueryString);
173    }else{
174  0 LOG.info("JPQL Query:"+finalQueryString);
175  0 query = em.createQuery(finalQueryString);
176    }
177   
178    //Set the pagination information (eg. only return 25 rows starting at row 100)
179  0 if(searchRequest.getStartAt()!=null){
180  0 query.setFirstResult(searchRequest.getStartAt().intValue());
181    }
182  0 if(searchRequest.getMaxResults()!=null){
183  0 query.setMaxResults(searchRequest.getMaxResults().intValue());
184    }
185   
186    //replace all the "." notation with "_" since the "."s in the ids of the queries will cause problems with the jpql
187  0 for (SearchParam searchParam : internalQueryParms) {
188    // check to see if optional param has any values set.
189  0 if (searchParam.getValue() != null) {
190  0 List<QueryParamInfo> queryParams = searchTypeInfo.getSearchCriteriaTypeInfo().getQueryParams();
191  0 String paramDataType = null;
192  0 if (queryParams != null) {
193  0 for (QueryParamInfo queryParam : queryParams) {
194  0 if (queryParam.getKey() != null && queryParam.getKey().equals(searchParam.getKey())) {
195  0 paramDataType = queryParam.getFieldDescriptor().getDataType();
196    }
197    }
198    }
199   
200  0 Object queryParamValue = null;
201  0 if ("date".equals(paramDataType) && searchParam.getValue() instanceof String) {
202  0 try {
203  0 queryParamValue = df.parse((String)searchParam.getValue());
204    } catch (ParseException e) {
205  0 throw new RuntimeException("Failed to parse date value " + searchParam.getValue());
206    }
207    } else {
208  0 queryParamValue = searchParam.getValue();
209    }
210    //Needed to get around Hibernate not supporting IN(:var) where var is null or an empty collection
211  0 if((queryParamValue==null||queryParamValue instanceof Collection && ((Collection<?>)queryParamValue).isEmpty())&&"list".equals(paramDataType)){
212  0 queryParamValue = "";
213    }
214  0 query.setParameter(searchParam.getKey().replace(".", "_"), queryParamValue);
215    }
216    }
217   
218    // Turn into results
219  0 List<SearchResultRow> results = convertToResults(query.getResultList(),searchTypeInfo);
220   
221  0 SearchResult searchResult = new SearchResult();
222  0 searchResult.setRows(results);
223  0 searchResult.setSortColumn(searchRequest.getSortColumn());
224  0 searchResult.setSortDirection(searchRequest.getSortDirection());
225  0 searchResult.setStartAt(searchRequest.getStartAt());
226  0 if(searchRequest.getNeededTotalResults()!=null && searchRequest.getNeededTotalResults()){
227    //Get count of total rows if needed
228  0 String regex = "^\\s*[Ss][Ee][Ll][Ee][Cc][Tt]\\s+([^,\\s]+)(.|[\r\n])*?\\s+[Ff][Rr][Oo][Mm]\\s+";
229  0 String replacement = "SELECT COUNT($1) FROM ";
230  0 String countQueryString = (queryString + optionalQueryString).replaceFirst(regex, replacement);
231   
232  0 LOG.info("Executing query: "+countQueryString);
233  0 Query countQuery;
234  0 if(isNative){
235  0 countQuery = em.createNativeQuery(countQueryString);
236    }else{
237  0 countQuery = em.createQuery(countQueryString);
238    }
239  0 for (SearchParam searchParam : internalQueryParms) {
240  0 countQuery.setParameter(searchParam.getKey().replace(".", "_"), searchParam.getValue());
241    }
242  0 Long totalResults = (Long) countQuery.getSingleResult();
243  0 searchResult.setTotalResults(totalResults.intValue());
244    }
245   
246  0 return searchResult;
247    }
248   
 
249  0 toggle private List<SearchResultRow> convertToResults(List<?> queryResults,
250    SearchTypeInfo searchTypeInfo) {
251  0 List<SearchResultRow> results = new ArrayList<SearchResultRow>();
252   
253  0 if(queryResults!=null){
254    //Copy the query results to a Result object
255  0 for(Object queryResult:queryResults){
256  0 SearchResultRow result = new SearchResultRow();
257  0 int i=0;
258  0 for (ResultColumnInfo resultColumn : searchTypeInfo.getSearchResultTypeInfo().getResultColumns()) {
259   
260  0 SearchResultCell resultCell = new SearchResultCell();
261  0 resultCell.setKey(resultColumn.getKey());
262   
263  0 try {
264  0 Object queryResultCell = null;
265  0 if(queryResult.getClass().isArray()){
266  0 queryResultCell = ((Object[])queryResult)[i];
267   
268    }else{
269  0 queryResultCell = queryResult;
270    }
271   
272  0 if (queryResultCell != null) {
273  0 resultCell.setValue(queryResultCell.toString());
274    }
275   
276    } catch (Exception e) {
277  0 throw new RuntimeException("Error copying results from " + queryResult.toString(),e);
278    }
279   
280  0 result.getCells().add(resultCell);
281  0 i++;
282    }
283  0 results.add(result);
284    }
285    }
286  0 return results;
287    }
288   
289    }