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.krad.uif.field;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.krad.uif.component.BindingInfo;
020import org.kuali.rice.krad.uif.component.MethodInvokerConfig;
021
022import java.io.Serializable;
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.List;
026import 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 */
043public 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}