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