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