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