| 1 |  |   | 
  | 2 |  |   | 
  | 3 |  |   | 
  | 4 |  |   | 
  | 5 |  |   | 
  | 6 |  |   | 
  | 7 |  |   | 
  | 8 |  |   | 
  | 9 |  |   | 
  | 10 |  |   | 
  | 11 |  |   | 
  | 12 |  |   | 
  | 13 |  |   | 
  | 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 | 0 |  public class AbstractSearchableCrudDaoImpl extends AbstractCrudDaoImpl | 
  | 40 |  |                  implements SearchableDao { | 
  | 41 | 0 |          final Logger LOG = Logger.getLogger(AbstractSearchableCrudDaoImpl.class); | 
  | 42 | 0 |      private static SimpleDateFormat df = new SimpleDateFormat("EEE MMM dd hh:mm:ss zzz yyyy"); | 
  | 43 |  |   | 
  | 44 |  |          @Override | 
  | 45 |  |          public SearchResult search(SearchRequest searchRequest,        Map<String, String> queryMap, SearchTypeInfo searchTypeInfo) { | 
  | 46 | 0 |                  String searchKey = searchRequest.getSearchKey(); | 
  | 47 |  |                   | 
  | 48 | 0 |                  boolean isNative = false; | 
  | 49 |  |                   | 
  | 50 |  |                   | 
  | 51 | 0 |                  String queryString = queryMap.get(searchKey); | 
  | 52 | 0 |                  String optionalQueryString = ""; | 
  | 53 | 0 |                  if(null == queryString){ | 
  | 54 | 0 |                          LOG.error("No SQL query was found for searchKey:"+searchKey); | 
  | 55 |  |                  } | 
  | 56 |  |                   | 
  | 57 | 0 |                  if(queryString.toUpperCase().startsWith("NATIVE:")){ | 
  | 58 | 0 |                          queryString = queryString.substring("NATIVE:".length()); | 
  | 59 | 0 |                          isNative = true; | 
  | 60 |  |                  } | 
  | 61 |  |                   | 
  | 62 |  |                   | 
  | 63 | 0 |                  List<SearchParam> searchParamsTemp = new ArrayList<SearchParam>(searchRequest.getParams()); | 
  | 64 |  |                   | 
  | 65 | 0 |                  List<SearchParam> internalQueryParms = new ArrayList<SearchParam>(searchRequest.getParams()); | 
  | 66 | 0 |                  for(SearchParam searchParam : searchParamsTemp){ | 
  | 67 | 0 |                          for(QueryParamInfo queryParam:searchTypeInfo.getSearchCriteriaTypeInfo().getQueryParams()){ | 
  | 68 |  |                                   | 
  | 69 | 0 |                                  if(queryParam.isOptional()&&queryParam.getKey().equals(searchParam.getKey())&&searchParam.getValue()!=null){ | 
  | 70 | 0 |                                          if(!optionalQueryString.isEmpty()){ | 
  | 71 | 0 |                                                  optionalQueryString += " AND "; | 
  | 72 |  |                                          } | 
  | 73 |  |                                           | 
  | 74 |  |                                           | 
  | 75 | 0 |                                          String condition = queryMap.get(searchParam.getKey()); | 
  | 76 | 0 |                                          if (condition.trim().startsWith("!!")) { | 
  | 77 | 0 |                                              String substitutionType = condition.trim().substring("!!".length()); | 
  | 78 |  |                                               | 
  | 79 | 0 |                                              if (condition.contains(" ")) { | 
  | 80 | 0 |                                                  substitutionType = condition.substring("!!".length(), condition.indexOf(" ")); | 
  | 81 |  |                                              } | 
  | 82 |  |                                               | 
  | 83 | 0 |                                              if (substitutionType != null && substitutionType.equals("NUMBER_RANGE")) { | 
  | 84 | 0 |                                                  String realCondition = condition.substring("!!".length() + substitutionType.length()).trim(); | 
  | 85 | 0 |                                                  String queryValue = (String)searchParam.getValue(); | 
  | 86 |  |                                                   | 
  | 87 | 0 |                                                  if (queryValue != null && queryValue.trim().contains("-")) { | 
  | 88 | 0 |                                                      StringTokenizer strTokenizer = new StringTokenizer(queryValue.trim(),"-"); | 
  | 89 | 0 |                                                      if (strTokenizer.hasMoreElements()) { | 
  | 90 | 0 |                                                          String strNum1 = strTokenizer.nextToken().trim(); | 
  | 91 | 0 |                                                          String strNum2 = strTokenizer.nextToken().trim(); | 
  | 92 | 0 |                                                          optionalQueryString +=  | 
  | 93 |  |                                                              realCondition + | 
  | 94 |  |                                                              " BETWEEN " + "'" + strNum1 + "'" + " AND " + "'" + strNum2 + "'"; | 
  | 95 | 0 |                                                          internalQueryParms.remove(searchParam); | 
  | 96 |  |                                                      } | 
  | 97 | 0 |                                                  } else { | 
  | 98 |  |                                                       | 
  | 99 | 0 |                                                      optionalQueryString += realCondition + " = '" + queryValue + "'"; | 
  | 100 | 0 |                                                      internalQueryParms.remove(searchParam); | 
  | 101 |  |                                                  } | 
  | 102 |  |                                              } | 
  | 103 | 0 |                                          } else if (condition.trim().contains(":")) { | 
  | 104 |  |                                               | 
  | 105 | 0 |                                              String dataType = queryParam.getFieldDescriptor().getDataType(); | 
  | 106 | 0 |                                              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 | 0 |                                                  optionalQueryString += queryMap.get(searchParam.getKey()); | 
  | 111 |  |                                              }                                                 | 
  | 112 | 0 |                                          } else { | 
  | 113 |  |                                                   | 
  | 114 |  |                                                   | 
  | 115 |  |                                                   | 
  | 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 | 0 |                                  } | 
  | 122 |  |                          } | 
  | 123 |  |                  } | 
  | 124 |  |                   | 
  | 125 |  |                   | 
  | 126 | 0 |                  if(!optionalQueryString.isEmpty()){ | 
  | 127 | 0 |                          if(!queryString.toUpperCase().contains(" WHERE ")){ | 
  | 128 | 0 |                                  queryString += " WHERE "; | 
  | 129 |  |                          } | 
  | 130 |  |                          else { | 
  | 131 | 0 |                                  queryString += " AND "; | 
  | 132 |  |                          } | 
  | 133 |  |                  } | 
  | 134 |  |                   | 
  | 135 |  |                   | 
  | 136 | 0 |                  String orderByClause = "";                 | 
  | 137 | 0 |                  if(!queryString.toUpperCase().contains("ORDER BY")&&searchRequest.getSortColumn()!=null){ | 
  | 138 |  |                           | 
  | 139 | 0 |                          int i = 0; | 
  | 140 |  |                           | 
  | 141 |  |                           | 
  | 142 | 0 |                          int selectIndex = queryString.toLowerCase().indexOf("select")+"select".length(); | 
  | 143 | 0 |                          int fromIndex = queryString.toLowerCase().indexOf(" from "); | 
  | 144 |  |                           | 
  | 145 | 0 |                          if (selectIndex >= 0 && fromIndex > selectIndex){ | 
  | 146 | 0 |                                  String[] jpqlResultColumns = queryString.substring(selectIndex, fromIndex).replaceAll("\\s", "").split(","); | 
  | 147 | 0 |                                  for(ResultColumnInfo results : searchTypeInfo.getSearchResultTypeInfo().getResultColumns()){ | 
  | 148 | 0 |                                          if(results.getKey().equals(searchRequest.getSortColumn())){ | 
  | 149 | 0 |                                                  orderByClause = " ORDER BY "+jpqlResultColumns[i]+" "; | 
  | 150 | 0 |                                                  if(searchRequest.getSortDirection()!=null && searchRequest.getSortDirection()==SortDirection.DESC){ | 
  | 151 | 0 |                                                          orderByClause += "DESC "; | 
  | 152 |  |                                                  }else{ | 
  | 153 | 0 |                                                          orderByClause += "ASC "; | 
  | 154 |  |                                                  } | 
  | 155 |  |                                          } | 
  | 156 | 0 |                                          i++; | 
  | 157 |  |                                  } | 
  | 158 |  |                          }                         | 
  | 159 |  |                  } | 
  | 160 |  |                   | 
  | 161 |  |                   | 
  | 162 | 0 |                  String finalQueryString = queryString + optionalQueryString + orderByClause; | 
  | 163 |  |                   | 
  | 164 |  |                   | 
  | 165 |  |                   | 
  | 166 |  |                   | 
  | 167 |  |                   | 
  | 168 |  |                  Query query; | 
  | 169 | 0 |                  if(isNative){ | 
  | 170 | 0 |                          LOG.info("Native Query:"+finalQueryString); | 
  | 171 | 0 |                          query = em.createNativeQuery(finalQueryString); | 
  | 172 |  |                  }else{ | 
  | 173 | 0 |                          LOG.info("JPQL Query:"+finalQueryString); | 
  | 174 | 0 |                          query = em.createQuery(finalQueryString); | 
  | 175 |  |                  } | 
  | 176 |  |                   | 
  | 177 |  |                   | 
  | 178 | 0 |                  if(searchRequest.getStartAt()!=null){ | 
  | 179 | 0 |                          query.setFirstResult(searchRequest.getStartAt().intValue()); | 
  | 180 |  |                  } | 
  | 181 | 0 |                  if(searchRequest.getMaxResults()!=null){ | 
  | 182 | 0 |                          query.setMaxResults(searchRequest.getMaxResults().intValue()); | 
  | 183 |  |                  } | 
  | 184 |  |                   | 
  | 185 |  |                   | 
  | 186 | 0 |                  for (SearchParam searchParam : internalQueryParms) { | 
  | 187 |  |                           | 
  | 188 | 0 |                          if (searchParam.getValue() != null) { | 
  | 189 | 0 |                              List<QueryParamInfo> queryParams = searchTypeInfo.getSearchCriteriaTypeInfo().getQueryParams(); | 
  | 190 | 0 |                              String paramDataType = null; | 
  | 191 | 0 |                              if (queryParams != null) { | 
  | 192 | 0 |                                  for (QueryParamInfo queryParam : queryParams) { | 
  | 193 | 0 |                                      if (queryParam.getKey() != null && queryParam.getKey().equals(searchParam.getKey())) { | 
  | 194 | 0 |                                          paramDataType = queryParam.getFieldDescriptor().getDataType(); | 
  | 195 |  |                                      } | 
  | 196 |  |                                  } | 
  | 197 |  |                              } | 
  | 198 |  |                               | 
  | 199 | 0 |                  Object queryParamValue = null; | 
  | 200 | 0 |                              if ("date".equals(paramDataType) && searchParam.getValue() instanceof String) { | 
  | 201 |  |                                  try { | 
  | 202 | 0 |                          queryParamValue = df.parse((String)searchParam.getValue()); | 
  | 203 | 0 |                      } catch (ParseException e) { | 
  | 204 | 0 |                          throw new RuntimeException("Failed to parse date value " + searchParam.getValue()); | 
  | 205 | 0 |                      } | 
  | 206 |  |                              } else { | 
  | 207 | 0 |                                  queryParamValue = searchParam.getValue(); | 
  | 208 |  |                              } | 
  | 209 |  |                               | 
  | 210 | 0 |                              query.setParameter(searchParam.getKey().replace(".", "_"), queryParamValue); | 
  | 211 | 0 |                          } | 
  | 212 |  |                  } | 
  | 213 |  |   | 
  | 214 |  |                   | 
  | 215 | 0 |                  List<SearchResultRow> results = convertToResults(query.getResultList(),searchTypeInfo); | 
  | 216 |  |   | 
  | 217 | 0 |                  SearchResult searchResult = new SearchResult(); | 
  | 218 | 0 |                  searchResult.setRows(results); | 
  | 219 | 0 |                  searchResult.setSortColumn(searchRequest.getSortColumn()); | 
  | 220 | 0 |                  searchResult.setSortDirection(searchRequest.getSortDirection()); | 
  | 221 | 0 |                  searchResult.setStartAt(searchRequest.getStartAt()); | 
  | 222 | 0 |                  if(searchRequest.getNeededTotalResults()!=null && searchRequest.getNeededTotalResults()){ | 
  | 223 |  |                           | 
  | 224 | 0 |                          String regex = "^\\s*[Ss][Ee][Ll][Ee][Cc][Tt]\\s+([^,\\s]+)(.|[\r\n])*?\\s+[Ff][Rr][Oo][Mm]\\s+"; | 
  | 225 | 0 |                          String replacement = "SELECT COUNT($1) FROM "; | 
  | 226 | 0 |                          String countQueryString = (queryString + optionalQueryString).replaceFirst(regex, replacement); | 
  | 227 |  |   | 
  | 228 | 0 |                          LOG.info("Executing query: "+countQueryString); | 
  | 229 |  |                          Query countQuery; | 
  | 230 | 0 |                          if(isNative){ | 
  | 231 | 0 |                                  countQuery = em.createNativeQuery(countQueryString); | 
  | 232 |  |                          }else{ | 
  | 233 | 0 |                                  countQuery = em.createQuery(countQueryString); | 
  | 234 |  |                          } | 
  | 235 | 0 |                          for (SearchParam searchParam : internalQueryParms) { | 
  | 236 | 0 |                                  countQuery.setParameter(searchParam.getKey().replace(".", "_"), searchParam.getValue()); | 
  | 237 |  |                          } | 
  | 238 | 0 |                          Long totalResults = (Long) countQuery.getSingleResult(); | 
  | 239 | 0 |                          searchResult.setTotalResults(totalResults.intValue()); | 
  | 240 |  |                  } | 
  | 241 |  |   | 
  | 242 | 0 |                  return searchResult; | 
  | 243 |  |          } | 
  | 244 |  |           | 
  | 245 |  |          private List<SearchResultRow> convertToResults(List<?> queryResults, | 
  | 246 |  |                          SearchTypeInfo searchTypeInfo) { | 
  | 247 | 0 |                  List<SearchResultRow> results = new ArrayList<SearchResultRow>(); | 
  | 248 |  |   | 
  | 249 | 0 |                  if(queryResults!=null){ | 
  | 250 |  |                           | 
  | 251 | 0 |                          for(Object queryResult:queryResults){ | 
  | 252 | 0 |                                  SearchResultRow result = new SearchResultRow(); | 
  | 253 | 0 |                                  int i=0; | 
  | 254 | 0 |                                  for (ResultColumnInfo resultColumn : searchTypeInfo.getSearchResultTypeInfo().getResultColumns()) { | 
  | 255 |  |                           | 
  | 256 | 0 |                                          SearchResultCell resultCell = new SearchResultCell(); | 
  | 257 | 0 |                                          resultCell.setKey(resultColumn.getKey()); | 
  | 258 |  |                                           | 
  | 259 |  |                                          try { | 
  | 260 | 0 |                                                  Object queryResultCell = null; | 
  | 261 | 0 |                                                  if(queryResult.getClass().isArray()){ | 
  | 262 | 0 |                                                          queryResultCell = ((Object[])queryResult)[i]; | 
  | 263 |  |                                                           | 
  | 264 |  |                                                  }else{ | 
  | 265 | 0 |                                                          queryResultCell = queryResult; | 
  | 266 |  |                                                  } | 
  | 267 |  |                                                   | 
  | 268 | 0 |                                                  if (queryResultCell != null) { | 
  | 269 | 0 |                                                          resultCell.setValue(queryResultCell.toString());                                                         | 
  | 270 |  |                                                  } | 
  | 271 |  |                                           | 
  | 272 | 0 |                                          } catch (Exception e) { | 
  | 273 | 0 |                                                  throw new RuntimeException("Error copying results from " + queryResult.toString(),e); | 
  | 274 | 0 |                                          } | 
  | 275 |  |                                           | 
  | 276 | 0 |                                          result.getCells().add(resultCell); | 
  | 277 | 0 |                                          i++; | 
  | 278 | 0 |                                  } | 
  | 279 | 0 |                                  results.add(result); | 
  | 280 | 0 |                          } | 
  | 281 |  |                  } | 
  | 282 | 0 |                  return results; | 
  | 283 |  |          } | 
  | 284 |  |   | 
  | 285 |  |  } |