001    /**
002     * Copyright 2005-2014 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     */
016    package org.kuali.rice.krad.uif.field;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.krad.uif.component.BindingInfo;
020    import org.kuali.rice.krad.uif.component.MethodInvokerConfig;
021    
022    import java.io.Serializable;
023    import java.util.ArrayList;
024    import java.util.HashMap;
025    import java.util.List;
026    import java.util.Map;
027    
028    /**
029     * Holds configuration for executing a dynamic query on an <code>InputField</code> to
030     * pull data for updating the UI
031     *
032     * <p>
033     * There are two types of query types that can be configured and executed. The first is provided
034     * completely by the framework using the <code>LookupService</code> and will perform a query
035     * against the configured dataObjectClassName using the query parameters and return field mapping.
036     * The second type will invoke a method that will perform the query. This can be configured using the
037     * queryMethodToCall (if the method is on the view helper service), or using the queryMethodInvoker if
038     * the method is on another class or object.
039     * </p>
040     *
041     * @author Kuali Rice Team (rice.collab@kuali.org)
042     */
043    public class AttributeQuery implements Serializable {
044        private static final long serialVersionUID = -4569905665441735255L;
045    
046        private String dataObjectClassName;
047    
048        private boolean renderNotFoundMessage;
049        private String returnMessageText;
050        private String returnMessageStyleClasses;
051    
052        private Map<String, String> queryFieldMapping;
053        private Map<String, String> returnFieldMapping;
054        private Map<String, String> additionalCriteria;
055    
056        private List<String> sortPropertyNames;
057    
058        private String queryMethodToCall;
059        private List<String> queryMethodArgumentFieldList;
060        private MethodInvokerConfig queryMethodInvokerConfig;
061    
062        public AttributeQuery() {
063            renderNotFoundMessage = true;
064            queryFieldMapping = new HashMap<String, String>();
065            returnFieldMapping = new HashMap<String, String>();
066            additionalCriteria = new HashMap<String, String>();
067            sortPropertyNames = new ArrayList<String>();
068            queryMethodArgumentFieldList = new ArrayList<String>();
069        }
070    
071        /**
072         * Adjusts the path on the query field mapping from property to match the binding
073         * path prefix of the given <code>BindingInfo</code>
074         *
075         * @param bindingInfo - binding info instance to copy binding path prefix from
076         */
077        public void updateQueryFieldMapping(BindingInfo bindingInfo) {
078            Map<String, String> adjustedQueryFieldMapping = new HashMap<String, String>();
079            for (String fromFieldPath : getQueryFieldMapping().keySet()) {
080                String toField = getQueryFieldMapping().get(fromFieldPath);
081                String adjustedFromFieldPath = bindingInfo.getPropertyAdjustedBindingPath(fromFieldPath);
082    
083                adjustedQueryFieldMapping.put(adjustedFromFieldPath, toField);
084            }
085    
086            this.queryFieldMapping = adjustedQueryFieldMapping;
087        }
088    
089        /**
090         * Adjusts the path on the return field mapping to property to match the binding
091         * path prefix of the given <code>BindingInfo</code>
092         *
093         * @param bindingInfo - binding info instance to copy binding path prefix from
094         */
095        public void updateReturnFieldMapping(BindingInfo bindingInfo) {
096            Map<String, String> adjustedReturnFieldMapping = new HashMap<String, String>();
097            for (String fromFieldPath : getReturnFieldMapping().keySet()) {
098                String toFieldPath = getReturnFieldMapping().get(fromFieldPath);
099                String adjustedToFieldPath = bindingInfo.getPropertyAdjustedBindingPath(toFieldPath);
100    
101                adjustedReturnFieldMapping.put(fromFieldPath, adjustedToFieldPath);
102            }
103    
104            this.returnFieldMapping = adjustedReturnFieldMapping;
105        }
106    
107        /**
108         * Adjusts the path on the query method arguments field list to match the binding
109         * path prefix of the given <code>BindingInfo</code>
110         *
111         * @param bindingInfo - binding info instance to copy binding path prefix from
112         */
113        public void updateQueryMethodArgumentFieldList(BindingInfo bindingInfo) {
114            List<String> adjustedArgumentFieldList = new ArrayList<String>();
115            for (String argumentFieldPath : getQueryMethodArgumentFieldList()) {
116                String adjustedFieldPath = bindingInfo.getPropertyAdjustedBindingPath(argumentFieldPath);
117                adjustedArgumentFieldList.add(adjustedFieldPath);
118            }
119    
120            this.queryMethodArgumentFieldList = adjustedArgumentFieldList;
121        }
122    
123        /**
124         * Builds String for passing the queryFieldMapping Map as a Javascript object
125         * parameter
126         *
127         * @return String js parameter string
128         */
129        public String getQueryFieldMappingJsString() {
130            String queryFieldMappingJs = "{";
131    
132            for (String queryField : queryFieldMapping.keySet()) {
133                if (!StringUtils.equals(queryFieldMappingJs, "{")) {
134                    queryFieldMappingJs += ",";
135                }
136    
137                queryFieldMappingJs += "\"" + queryField + "\":\"" + queryFieldMapping.get(queryField) + "\"";
138            }
139    
140            queryFieldMappingJs += "}";
141    
142            return queryFieldMappingJs;
143        }
144    
145        /**
146         * Builds String for passing the returnFieldMapping Map as a Javascript object
147         * parameter
148         *
149         * @return String js parameter string
150         */
151        public String getReturnFieldMappingJsString() {
152            String returnFieldMappingJs = "{";
153    
154            for (String fromField : returnFieldMapping.keySet()) {
155                if (!StringUtils.equals(returnFieldMappingJs, "{")) {
156                    returnFieldMappingJs += ",";
157                }
158    
159                returnFieldMappingJs += "\"" + returnFieldMapping.get(fromField) + "\":\"" + fromField + "\"";
160            }
161    
162            returnFieldMappingJs += "}";
163    
164            return returnFieldMappingJs;
165        }
166    
167        /**
168         * Builds String for passing the queryMethodArgumentFieldList as a Javascript array
169         *
170         * @return String js parameter string
171         */
172        public String getQueryMethodArgumentFieldsJsString() {
173            String queryMethodArgsJs = "[";
174    
175            for (String methodArg : queryMethodArgumentFieldList) {
176                if (!StringUtils.equals(queryMethodArgsJs, "{")) {
177                    queryMethodArgsJs += ",";
178                }
179                queryMethodArgsJs += "\"" + methodArg + "\"";
180            }
181    
182            queryMethodArgsJs += "]";
183    
184            return queryMethodArgsJs;
185        }
186    
187        /**
188         * Indicates whether this attribute query is configured to invoke a custom
189         * method as opposed to running the general object query. If either the query method
190         * to call is given, or the query method invoker is not null it is assumed the
191         * intention is to call a custom method
192         *
193         * @return boolean true if a custom method is configured, false if not
194         */
195        public boolean hasConfiguredMethod() {
196            boolean configuredMethod = false;
197    
198            if (StringUtils.isNotBlank(getQueryMethodToCall())) {
199                configuredMethod = true;
200            } else if (getQueryMethodInvokerConfig() != null) {
201                configuredMethod = true;
202            }
203    
204            return configuredMethod;
205        }
206    
207        /**
208         * Class name for the data object the query should be performed against
209         *
210         * @return String data object class name
211         */
212        public String getDataObjectClassName() {
213            return dataObjectClassName;
214        }
215    
216        /**
217         * Setter for the query data object class name
218         *
219         * @param dataObjectClassName
220         */
221        public void setDataObjectClassName(String dataObjectClassName) {
222            this.dataObjectClassName = dataObjectClassName;
223        }
224    
225        /**
226         * Configures the query parameters by mapping fields in the view
227         * to properties on the data object class for the query
228         *
229         * <p>
230         * Each map entry configures one parameter for the query, where
231         * the map key is the field name to pull the value from, and the
232         * map value is the property name on the object the parameter should
233         * populate.
234         * </p>
235         *
236         * @return Map<String, String> mapping of query parameters
237         */
238        public Map<String, String> getQueryFieldMapping() {
239            return queryFieldMapping;
240        }
241    
242        /**
243         * Setter for the query parameter mapping
244         *
245         * @param queryFieldMapping
246         */
247        public void setQueryFieldMapping(Map<String, String> queryFieldMapping) {
248            this.queryFieldMapping = queryFieldMapping;
249        }
250    
251        /**
252         * Maps properties from the result object of the query to
253         * fields in the view
254         *
255         * <p>
256         * Each map entry configures one return mapping, where the map
257         * key is the field name for the field to populate, and the map
258         * values is the name of the property on the result object to
259         * pull the value from
260         * </p>
261         *
262         * @return Map<String, String> return field mapping
263         */
264        public Map<String, String> getReturnFieldMapping() {
265            return returnFieldMapping;
266        }
267    
268        /**
269         * Setter for the return field mapping
270         *
271         * @param returnFieldMapping
272         */
273        public void setReturnFieldMapping(Map<String, String> returnFieldMapping) {
274            this.returnFieldMapping = returnFieldMapping;
275        }
276    
277        /**
278         * Fixed criteria that will be appended to the dynamic criteria generated
279         * for the query. Map key gives name of the property the criteria should
280         * apply to, and the map value is the value (literal) for the criteria. Standard
281         * lookup wildcards are allowed
282         *
283         * @return Map<String, String> field name/value pairs for query criteria
284         */
285        public Map<String, String> getAdditionalCriteria() {
286            return additionalCriteria;
287        }
288    
289        /**
290         * Setter for the query's additional criteria map
291         *
292         * @param additionalCriteria
293         */
294        public void setAdditionalCriteria(Map<String, String> additionalCriteria) {
295            this.additionalCriteria = additionalCriteria;
296        }
297    
298        /**
299         * List of property names to sort the query results by. The sort
300         * will be performed on each property in the order they are contained
301         * within the list. Each property must be a valid property of the
302         * return query object (the data object in case of the general query)
303         *
304         * @return List<String> property names
305         */
306        public List<String> getSortPropertyNames() {
307            return sortPropertyNames;
308        }
309    
310        /**
311         * Setter for the list of property names to sort results by
312         *
313         * @param sortPropertyNames
314         */
315        public void setSortPropertyNames(List<String> sortPropertyNames) {
316            this.sortPropertyNames = sortPropertyNames;
317        }
318    
319        /**
320         * Indicates whether a message should be added to the query result
321         * object and displayed when the query return object is null
322         *
323         * @return boolean true if not found message should be added, false otherwise
324         */
325        public boolean isRenderNotFoundMessage() {
326            return renderNotFoundMessage;
327        }
328    
329        /**
330         * Setter for the render not found message indicator
331         *
332         * @param renderNotFoundMessage
333         */
334        public void setRenderNotFoundMessage(boolean renderNotFoundMessage) {
335            this.renderNotFoundMessage = renderNotFoundMessage;
336        }
337    
338        /**
339         * Message text to display along with the query result
340         *
341         * @return String literal message text
342         */
343        public String getReturnMessageText() {
344            return returnMessageText;
345        }
346    
347        /**
348         * Setter for the return message text
349         *
350         * @param returnMessageText
351         */
352        public void setReturnMessageText(String returnMessageText) {
353            this.returnMessageText = returnMessageText;
354        }
355    
356        /**
357         * CSS Style classes that should be applied to the return message.
358         * Multiple style classes should be delimited by a space
359         *
360         * @return String style classes
361         */
362        public String getReturnMessageStyleClasses() {
363            return returnMessageStyleClasses;
364        }
365    
366        /**
367         * Setter for the return messages style classes
368         *
369         * @param returnMessageStyleClasses
370         */
371        public void setReturnMessageStyleClasses(String returnMessageStyleClasses) {
372            this.returnMessageStyleClasses = returnMessageStyleClasses;
373        }
374    
375        /**
376         * Configures the name of the method that should be invoked to perform
377         * the query
378         *
379         * <p>
380         * Should contain only the method name (no parameters or return type). If only
381         * the query method name is configured it is assumed to be on the <code>ViewHelperService</code>
382         * for the contained view.
383         * </p>
384         *
385         * @return String query method name
386         */
387        public String getQueryMethodToCall() {
388            return queryMethodToCall;
389        }
390    
391        /**
392         * Setter for the query method name
393         *
394         * @param queryMethodToCall
395         */
396        public void setQueryMethodToCall(String queryMethodToCall) {
397            this.queryMethodToCall = queryMethodToCall;
398        }
399    
400        /**
401         * List of field names that should be passed as arguments to the query method
402         *
403         * <p>
404         * Each entry in the list maps to a method parameter, in the other contained within
405         * the list. The value for the field within the view will be pulled and passed
406         * to the query method as an argument
407         * </p>
408         *
409         * @return List<String> query method argument list
410         */
411        public List<String> getQueryMethodArgumentFieldList() {
412            return queryMethodArgumentFieldList;
413        }
414    
415        /**
416         * Setter for the query method argument list
417         *
418         * @param queryMethodArgumentFieldList
419         */
420        public void setQueryMethodArgumentFieldList(List<String> queryMethodArgumentFieldList) {
421            this.queryMethodArgumentFieldList = queryMethodArgumentFieldList;
422        }
423    
424        /**
425         * Configures the query method target class/object and method name
426         *
427         * <p>
428         * When the query method is not contained on the <code>ViewHelperService</code>, this
429         * can be configured for declaring the target class/object and method. The target class
430         * can be set in which case a new instance will be created and the given method invoked.
431         * Alternatively, the target object instance for the invocation can be given. Or finally
432         * a static method can be configured
433         * </p>
434         *
435         * @return MethodInvokerConfig query method config
436         */
437        public MethodInvokerConfig getQueryMethodInvokerConfig() {
438            return queryMethodInvokerConfig;
439        }
440    
441        /**
442         * Setter for the query method config
443         *
444         * @param queryMethodInvokerConfig
445         */
446        public void setQueryMethodInvokerConfig(MethodInvokerConfig queryMethodInvokerConfig) {
447            this.queryMethodInvokerConfig = queryMethodInvokerConfig;
448        }
449    }