Clover Coverage Report - KS Common Impl 1.2-M4-SNAPSHOT
Coverage timestamp: Wed Jul 20 2011 12:37:18 EDT
../../../../../../img/srcFileCovDistChart0.png 55% of files have more coverage
131   295   51   65.5
62   219   0.39   2
2     25.5  
1    
 
  AbstractSearchableCrudDaoImpl       Line # 40 131 0% 51 195 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(),e);
206    }
207  0 } if ("long".equals(paramDataType) && searchParam.getValue() instanceof String) {
208  0 try{
209  0 queryParamValue = Long.valueOf((String)searchParam.getValue());
210    } catch (NumberFormatException e) {
211  0 throw new RuntimeException("Failed to parse date value " + searchParam.getValue(),e);
212    }
213    } else {
214  0 queryParamValue = searchParam.getValue();
215    }
216    //Needed to get around Hibernate not supporting IN(:var) where var is null or an empty collection
217  0 if((queryParamValue==null||queryParamValue instanceof Collection && ((Collection<?>)queryParamValue).isEmpty())&&"list".equals(paramDataType)){
218  0 queryParamValue = "";
219    }
220  0 query.setParameter(searchParam.getKey().replace(".", "_"), queryParamValue);
221    }
222    }
223   
224    // Turn into results
225  0 List<SearchResultRow> results = convertToResults(query.getResultList(),searchTypeInfo);
226   
227  0 SearchResult searchResult = new SearchResult();
228  0 searchResult.setRows(results);
229  0 searchResult.setSortColumn(searchRequest.getSortColumn());
230  0 searchResult.setSortDirection(searchRequest.getSortDirection());
231  0 searchResult.setStartAt(searchRequest.getStartAt());
232  0 if(searchRequest.getNeededTotalResults()!=null && searchRequest.getNeededTotalResults()){
233    //Get count of total rows if needed
234  0 String regex = "^\\s*[Ss][Ee][Ll][Ee][Cc][Tt]\\s+([^,\\s]+)(.|[\r\n])*?\\s+[Ff][Rr][Oo][Mm]\\s+";
235  0 String replacement = "SELECT COUNT($1) FROM ";
236  0 String countQueryString = (queryString + optionalQueryString).replaceFirst(regex, replacement);
237   
238  0 LOG.info("Executing query: "+countQueryString);
239  0 Query countQuery;
240  0 if(isNative){
241  0 countQuery = em.createNativeQuery(countQueryString);
242    }else{
243  0 countQuery = em.createQuery(countQueryString);
244    }
245  0 for (SearchParam searchParam : internalQueryParms) {
246  0 countQuery.setParameter(searchParam.getKey().replace(".", "_"), searchParam.getValue());
247    }
248  0 Long totalResults = (Long) countQuery.getSingleResult();
249  0 searchResult.setTotalResults(totalResults.intValue());
250    }
251   
252  0 return searchResult;
253    }
254   
 
255  0 toggle private List<SearchResultRow> convertToResults(List<?> queryResults,
256    SearchTypeInfo searchTypeInfo) {
257  0 List<SearchResultRow> results = new ArrayList<SearchResultRow>();
258   
259  0 if(queryResults!=null){
260    //Copy the query results to a Result object
261  0 for(Object queryResult:queryResults){
262  0 SearchResultRow result = new SearchResultRow();
263  0 int i=0;
264  0 for (ResultColumnInfo resultColumn : searchTypeInfo.getSearchResultTypeInfo().getResultColumns()) {
265   
266  0 SearchResultCell resultCell = new SearchResultCell();
267  0 resultCell.setKey(resultColumn.getKey());
268   
269  0 try {
270  0 Object queryResultCell = null;
271  0 if(queryResult.getClass().isArray()){
272  0 queryResultCell = ((Object[])queryResult)[i];
273   
274    }else{
275  0 queryResultCell = queryResult;
276    }
277   
278  0 if (queryResultCell != null) {
279  0 resultCell.setValue(queryResultCell.toString());
280    }
281   
282    } catch (Exception e) {
283  0 throw new RuntimeException("Error copying results from " + queryResult.toString(),e);
284    }
285   
286  0 result.getCells().add(resultCell);
287  0 i++;
288    }
289  0 results.add(result);
290    }
291    }
292  0 return results;
293    }
294   
295    }