001 /**
002 * Copyright 2005-2011 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 }