001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kns.web.struts.form; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.kns.lookup.HtmlData; 020import org.kuali.rice.kns.lookup.LookupUtils; 021import org.kuali.rice.kns.util.PagingBannerUtils; 022import org.kuali.rice.krad.util.KRADConstants; 023 024import javax.servlet.http.HttpServletRequest; 025import java.util.Enumeration; 026import java.util.HashSet; 027import java.util.Map; 028import java.util.Set; 029 030/** 031 * Form to handle multiple value lookups 032 * 033 * @author Kuali Rice Team (rice.collab@kuali.org) 034 * 035 */ 036public class MultipleValueLookupForm extends LookupForm { 037 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MultipleValueLookupForm.class); 038 039 private KualiTableRenderFormMetadata tableMetadata; 040 041 private String lookupResultsSequenceNumber; 042 043 /** 044 * @see LookupForm#addRequiredNonEditableProperties() 045 */ 046 @Override 047 public void addRequiredNonEditableProperties(){ 048 super.addRequiredNonEditableProperties(); 049 registerRequiredNonEditableProperty(KRADConstants.LOOKUP_RESULTS_SEQUENCE_NUMBER); 050 registerRequiredNonEditableProperty(KRADConstants.LOOKED_UP_COLLECTION_NAME); 051 } 052 053 /** 054 * The number of rows that match the query criteria 055 */ 056 private int resultsActualSize; 057 058 /** 059 * The number of rows that match the query criteria or 060 * the max results limit size (if applicable), whichever is less 061 */ 062 private int resultsLimitedSize; 063 064 /** 065 * when the looked results screen was rendered, the index of the column that the results were sorted on. -1 for unknown, index numbers 066 * starting at 0 067 */ 068 private String previouslySortedColumnIndex; 069 070 /** 071 * Comment for <code>columnToSortIndex</code> 072 */ 073 private int columnToSortIndex; 074 075 /** 076 * the name of the collection being looked up by the calling page. This value will be returned unmodified to the 077 * calling page (indicated by super.getBackLocation()), which should use it to determine in which collection the 078 * selected results will be returned. 079 */ 080 private String lookedUpCollectionName; 081 082 /** 083 * Those object IDs that were selected before the current page is rendered 084 */ 085 private Set<String> previouslySelectedObjectIdSet; 086 /** 087 * Those object IDs that are rendered on the current page 088 */ 089 private Set<String> displayedObjectIdSet; 090 /** 091 * Those object IDs that are selected/checked on the current page 092 */ 093 private Set<String> selectedObjectIdSet; 094 /** 095 * The object IDs that are selected after the struts action is complete; the obj IDs in the keys of this Map will be considered checked in the UI 096 */ 097 private Map<String, String> compositeObjectIdMap; 098 099 public MultipleValueLookupForm() { 100 tableMetadata = new KualiTableRenderFormMetadata(); 101 setHtmlDataType(HtmlData.INPUT_HTML_DATA_TYPE); 102 } 103 104 @Override 105 public void populate(HttpServletRequest request) { 106 super.populate(request); 107 108 if (StringUtils.isNotBlank(request.getParameter(KRADConstants.TableRenderConstants.VIEWED_PAGE_NUMBER))) { 109 setViewedPageNumber(Integer.parseInt(request.getParameter(KRADConstants.TableRenderConstants.VIEWED_PAGE_NUMBER))); 110 } 111 else { 112 setViewedPageNumber(0); // first page is page 0 113 } 114 115 if (KRADConstants.TableRenderConstants.SWITCH_TO_PAGE_METHOD.equals(getMethodToCall())) { 116 final String paramPrefix = KRADConstants.DISPATCH_REQUEST_PARAMETER + "." + KRADConstants.TableRenderConstants.SWITCH_TO_PAGE_METHOD + "."; 117 setSwitchToPageNumber(PagingBannerUtils.getNumbericalValueAfterPrefix(paramPrefix, request.getParameterNames())); 118 if (getSwitchToPageNumber() == -1) { 119 throw new RuntimeException("Couldn't find page number"); 120 } 121 } 122 123 if (KRADConstants.TableRenderConstants.SORT_METHOD.equals(getMethodToCall())) { 124 final String paramPrefix = KRADConstants.DISPATCH_REQUEST_PARAMETER + "." + KRADConstants.TableRenderConstants.SORT_METHOD + "."; 125 setColumnToSortIndex( 126 PagingBannerUtils.getNumbericalValueAfterPrefix(paramPrefix, request.getParameterNames())); 127 if (getColumnToSortIndex() == -1) { 128 throw new RuntimeException("Couldn't find column to sort"); 129 } 130 } 131 132 setPreviouslySelectedObjectIdSet(parsePreviouslySelectedObjectIds(request)); 133 setSelectedObjectIdSet(parseSelectedObjectIdSet(request)); 134 setDisplayedObjectIdSet(parseDisplayedObjectIdSet(request)); 135 136 setSearchUsingOnlyPrimaryKeyValues(parseSearchUsingOnlyPrimaryKeyValues(request)); 137 if (isSearchUsingOnlyPrimaryKeyValues()) { 138 setPrimaryKeyFieldLabels(getLookupable().getPrimaryKeyFieldLabels()); 139 } 140 } 141 142 143 144 /** 145 * This method converts the composite object IDs into a String 146 * @return 147 */ 148 public String getCompositeSelectedObjectIds() { 149 return LookupUtils.convertSetOfObjectIdsToString(getCompositeObjectIdMap().keySet()); 150 } 151 152 protected Set<String> parsePreviouslySelectedObjectIds(HttpServletRequest request) { 153 String previouslySelectedObjectIds = request.getParameter(KRADConstants.MULTIPLE_VALUE_LOOKUP_PREVIOUSLY_SELECTED_OBJ_IDS_PARAM); 154 return LookupUtils.convertStringOfObjectIdsToSet(previouslySelectedObjectIds); 155 } 156 157 protected Set<String> parseSelectedObjectIdSet(HttpServletRequest request) { 158 Set<String> set = new HashSet<String>(); 159 160 Enumeration paramNames = request.getParameterNames(); 161 while (paramNames.hasMoreElements()) { 162 String paramName = (String) paramNames.nextElement(); 163 if (paramName.startsWith(KRADConstants.MULTIPLE_VALUE_LOOKUP_SELECTED_OBJ_ID_PARAM_PREFIX) && StringUtils.isNotBlank(request.getParameter(paramName))) { 164 set.add(StringUtils.substringAfter(paramName, KRADConstants.MULTIPLE_VALUE_LOOKUP_SELECTED_OBJ_ID_PARAM_PREFIX)); 165 } 166 } 167 return set; 168 } 169 170 protected Set<String> parseDisplayedObjectIdSet(HttpServletRequest request) { 171 Set<String> set = new HashSet<String>(); 172 173 Enumeration paramNames = request.getParameterNames(); 174 while (paramNames.hasMoreElements()) { 175 String paramName = (String) paramNames.nextElement(); 176 if (paramName.startsWith(KRADConstants.MULTIPLE_VALUE_LOOKUP_DISPLAYED_OBJ_ID_PARAM_PREFIX) && StringUtils.isNotBlank(request.getParameter(paramName))) { 177 set.add(StringUtils.substringAfter(paramName, KRADConstants.MULTIPLE_VALUE_LOOKUP_DISPLAYED_OBJ_ID_PARAM_PREFIX)); 178 } 179 } 180 return set; 181 } 182 183 /** 184 * Iterates through the request params, looks for the parameter representing the method to call in the format like 185 * methodToCall.sort.1.(::;true;::).x, and returns the boolean value in the (::; and ;::) delimiters. 186 * 187 * @see MultipleValueLookupForm#parseSearchUsingOnlyPrimaryKeyValues(String) 188 * 189 * @param request 190 * @return 191 */ 192 protected boolean parseSearchUsingOnlyPrimaryKeyValues(HttpServletRequest request) { 193 // the param we're looking for looks like: methodToCall.sort.1.(::;true;::).x , we want to parse out the "true" component 194 String paramPrefix = KRADConstants.DISPATCH_REQUEST_PARAMETER + "." + getMethodToCall() + "."; 195 for (Enumeration i = request.getParameterNames(); i.hasMoreElements();) { 196 String parameterName = (String) i.nextElement(); 197 if (parameterName.startsWith(paramPrefix) && parameterName.endsWith(".x")) { 198 return parseSearchUsingOnlyPrimaryKeyValues(parameterName); 199 } 200 } 201 // maybe doing an initial search, so no value will be present 202 return false; 203 } 204 205 /** 206 * Parses the method to call parameter passed in as a post parameter 207 * 208 * The parameter should be something like methodToCall.sort.1.(::;true;::).x, this method will return the value 209 * between (::; and ;::) as a boolean 210 * 211 * @param methodToCallParam the method to call in a format described above 212 * @return the value between the delimiters, false if there are no delimiters 213 */ 214 protected boolean parseSearchUsingOnlyPrimaryKeyValues(String methodToCallParam) { 215 String searchUsingOnlyPrimaryKeyValuesStr = StringUtils.substringBetween(methodToCallParam, KRADConstants.METHOD_TO_CALL_PARM12_LEFT_DEL, KRADConstants.METHOD_TO_CALL_PARM12_RIGHT_DEL); 216 if (StringUtils.isBlank(searchUsingOnlyPrimaryKeyValuesStr)) { 217 return false; 218 } 219 return Boolean.parseBoolean(searchUsingOnlyPrimaryKeyValuesStr); 220 } 221 222 public int getViewedPageNumber() { 223 return tableMetadata.getViewedPageNumber(); 224 } 225 226 public void setViewedPageNumber(int pageNumberBeingViewedForMultivalueLookups) { 227 tableMetadata.setViewedPageNumber(pageNumberBeingViewedForMultivalueLookups); 228 } 229 230 public String getLookupResultsSequenceNumber() { 231 return lookupResultsSequenceNumber; 232 } 233 234 public void setLookupResultsSequenceNumber(String lookupResultSequenceNumber) { 235 this.lookupResultsSequenceNumber = lookupResultSequenceNumber; 236 } 237 238 public int getTotalNumberOfPages() { 239 return tableMetadata.getTotalNumberOfPages(); 240 } 241 242 public void setTotalNumberOfPages(int totalNumberOfPages) { 243 tableMetadata.setTotalNumberOfPages(totalNumberOfPages); 244 } 245 246 public int getFirstRowIndex() { 247 return tableMetadata.getFirstRowIndex(); 248 } 249 250 public void setFirstRowIndex(int firstRowIndex) { 251 tableMetadata.setFirstRowIndex(firstRowIndex); 252 } 253 254 public int getLastRowIndex() { 255 return tableMetadata.getLastRowIndex(); 256 } 257 258 public void setLastRowIndex(int lastRowIndex) { 259 tableMetadata.setLastRowIndex(lastRowIndex); 260 } 261 262 public int getSwitchToPageNumber() { 263 return tableMetadata.getSwitchToPageNumber(); 264 } 265 266 protected void setSwitchToPageNumber(int switchToPageNumber) { 267 tableMetadata.setSwitchToPageNumber(switchToPageNumber); 268 } 269 270 public Set<String> getPreviouslySelectedObjectIdSet() { 271 return previouslySelectedObjectIdSet; 272 } 273 274 public void setPreviouslySelectedObjectIdSet(Set<String> previouslySelectedObjectIds) { 275 this.previouslySelectedObjectIdSet = previouslySelectedObjectIds; 276 } 277 278 public Set<String> getSelectedObjectIdSet() { 279 return selectedObjectIdSet; 280 } 281 282 public void setSelectedObjectIdSet(Set<String> selectedObjectIdSet) { 283 this.selectedObjectIdSet = selectedObjectIdSet; 284 } 285 286 public Set<String> getDisplayedObjectIdSet() { 287 return displayedObjectIdSet; 288 } 289 290 public void setDisplayedObjectIdSet(Set<String> displayedObjectIdSet) { 291 this.displayedObjectIdSet = displayedObjectIdSet; 292 } 293 294 public Map<String, String> getCompositeObjectIdMap() { 295 return compositeObjectIdMap; 296 } 297 298 public void setCompositeObjectIdMap(Map<String, String> compositeObjectIdMap) { 299 this.compositeObjectIdMap = compositeObjectIdMap; 300 } 301 302 public int getColumnToSortIndex() { 303 return columnToSortIndex; 304 } 305 306 public void setColumnToSortIndex(int columnToSortIndex) { 307 this.columnToSortIndex = columnToSortIndex; 308 } 309 310 public String getPreviouslySortedColumnIndex() { 311 return previouslySortedColumnIndex; 312 } 313 314 public void setPreviouslySortedColumnIndex(String previouslySortedColumnIndex) { 315 this.previouslySortedColumnIndex = previouslySortedColumnIndex; 316 } 317 318 /** 319 * gets the name of the collection being looked up by the calling page. This value will be returned unmodified to the 320 * calling page (indicated by super.getBackLocation()), which should use it to determine in which collection the 321 * selected results will be returned. 322 * 323 * @return 324 */ 325 public String getLookedUpCollectionName() { 326 return lookedUpCollectionName; 327 } 328 329 /** 330 * sets the name of the collection being looked up by the calling page. This value will be returned unmodified to the 331 * calling page (indicated by super.getBackLocation()), which should use it to determine in which collection the 332 * selected results will be returned 333 * 334 * @param lookedUpCollectionName 335 */ 336 public void setLookedUpCollectionName(String lookedUpCollectionName) { 337 this.lookedUpCollectionName = lookedUpCollectionName; 338 } 339 340 public int getResultsActualSize() { 341 return resultsActualSize; 342 } 343 344 public void setResultsActualSize(int resultsActualSize) { 345 this.resultsActualSize = resultsActualSize; 346 } 347 348 public int getResultsLimitedSize() { 349 return resultsLimitedSize; 350 } 351 352 public void setResultsLimitedSize(int resultsLimitedSize) { 353 this.resultsLimitedSize = resultsLimitedSize; 354 } 355 356 public void jumpToFirstPage(int listSize, int maxRowsPerPage) { 357 tableMetadata.jumpToFirstPage(listSize, maxRowsPerPage); 358 } 359 360 public void jumpToLastPage(int listSize, int maxRowsPerPage) { 361 tableMetadata.jumpToLastPage(listSize, maxRowsPerPage); 362 } 363 364 public void jumpToPage(int pageNumber, int listSize, int maxRowsPerPage) { 365 tableMetadata.jumpToPage(pageNumber, listSize, maxRowsPerPage); 366 } 367}