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}