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.widget;
017
018 import org.apache.commons.lang.StringUtils;
019 import org.kuali.rice.krad.bo.DataObjectRelationship;
020 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
021 import org.kuali.rice.krad.uif.UifParameters;
022 import org.kuali.rice.krad.uif.container.CollectionGroup;
023 import org.kuali.rice.krad.uif.field.InputField;
024 import org.kuali.rice.krad.uif.view.View;
025 import org.kuali.rice.krad.uif.component.BindingInfo;
026 import org.kuali.rice.krad.uif.component.Component;
027 import org.kuali.rice.krad.uif.field.ActionField;
028 import org.kuali.rice.krad.uif.util.ViewModelUtils;
029 import org.kuali.rice.krad.util.KRADUtils;
030
031 import java.util.HashMap;
032 import java.util.List;
033 import java.util.Map;
034
035 /**
036 * Widget for navigating to a lookup from a field (called a quickfinder)
037 *
038 * @author Kuali Rice Team (rice.collab@kuali.org)
039 */
040 public class QuickFinder extends WidgetBase {
041 private static final long serialVersionUID = 3302390972815386785L;
042
043 // lookup configuration
044 private String baseLookupUrl;
045 private String dataObjectClassName;
046 private String viewName;
047
048 private String referencesToRefresh;
049
050 private Map<String, String> fieldConversions;
051 private Map<String, String> lookupParameters;
052
053 // lookup view options
054 private String readOnlySearchFields;
055
056 private Boolean hideReturnLink;
057 private Boolean suppressActions;
058 private Boolean autoSearch;
059 private Boolean lookupCriteriaEnabled;
060 private Boolean supplementalActionsEnabled;
061 private Boolean disableSearchButtons;
062 private Boolean headerBarEnabled;
063 private Boolean showMaintenanceLinks;
064
065 private Boolean multipleValuesSelect;
066 private String lookupCollectionName;
067
068 private ActionField quickfinderActionField;
069
070 public QuickFinder() {
071 super();
072
073 fieldConversions = new HashMap<String, String>();
074 lookupParameters = new HashMap<String, String>();
075 }
076
077 /**
078 * The following finalization is performed:
079 *
080 * <ul>
081 * <li>
082 * Sets defaults on collectionLookup such as collectionName, and the class if not set
083 *
084 * <p>
085 * If the data object class was not configured for the lookup, the class configured for the collection group will
086 * be used if it has a lookup defined. If not data object class is found for the lookup it will be disabled. The
087 * collection name is also defaulted to the binding path for this collection group, so the results returned from
088 * the lookup will populate this collection. Finally field conversions will be generated based on the PK fields of
089 * the collection object class
090 * </p>
091 * </li>
092 * </ul>
093 *
094 * @see org.kuali.rice.krad.uif.widget.Widget#performFinalize(org.kuali.rice.krad.uif.view.View,
095 * java.lang.Object, org.kuali.rice.krad.uif.component.Component)
096 */
097 @Override
098 public void performFinalize(View view, Object model, Component parent) {
099 super.performFinalize(view, model, parent);
100
101 if (!isRender()) {
102 return;
103 }
104
105 if (parent instanceof InputField) {
106 InputField field = (InputField) parent;
107
108 // determine lookup class, field conversions and lookup parameters in
109 // not set
110 if (StringUtils.isBlank(dataObjectClassName)) {
111 DataObjectRelationship relationship = getRelationshipForField(view, model, field);
112
113 // if no relationship found cannot have a quickfinder
114 if (relationship == null) {
115 setRender(false);
116 return;
117 }
118
119 dataObjectClassName = relationship.getRelatedClass().getName();
120
121 if ((fieldConversions == null) || fieldConversions.isEmpty()) {
122 generateFieldConversions(field, relationship);
123 }
124
125 if ((lookupParameters == null) || lookupParameters.isEmpty()) {
126 generateLookupParameters(field, relationship);
127 }
128 }
129
130 // adjust paths based on associated attribute field
131 updateFieldConversions(field.getBindingInfo());
132 updateLookupParameters(field.getBindingInfo());
133 } else if (parent instanceof CollectionGroup) {
134 CollectionGroup collectionGroup = (CollectionGroup) parent;
135
136 // check to see if data object class is configured for lookup, if so we will assume it should be enabled
137 // if not and the class configured for the collection group is lookupable, use that
138 if (StringUtils.isBlank(getDataObjectClassName())) {
139 Class<?> collectionObjectClass = collectionGroup.getCollectionObjectClass();
140 boolean isCollectionClassLookupable = KRADServiceLocatorWeb.getViewDictionaryService().isLookupable(
141 collectionObjectClass);
142 if (isCollectionClassLookupable) {
143 setDataObjectClassName(collectionObjectClass.getName());
144
145 if ((fieldConversions == null) || fieldConversions.isEmpty()) {
146 // use PK fields for collection class
147 List<String> collectionObjectPKFields =
148 KRADServiceLocatorWeb.getDataObjectMetaDataService().listPrimaryKeyFieldNames(
149 collectionObjectClass);
150
151 for (String pkField : collectionObjectPKFields) {
152 fieldConversions.put(pkField, pkField);
153 }
154 }
155 } else {
156 // no available data object class to lookup so need to disable quickfinder
157 setRender(false);
158 }
159 }
160
161 // set the lookup return collection name to this collection path
162 if (isRender() && StringUtils.isBlank(getLookupCollectionName())) {
163 setLookupCollectionName(collectionGroup.getBindingInfo().getBindingPath());
164 }
165 }
166
167 quickfinderActionField.addActionParameter(UifParameters.BASE_LOOKUP_URL, baseLookupUrl);
168 quickfinderActionField.addActionParameter(UifParameters.DATA_OBJECT_CLASS_NAME, dataObjectClassName);
169
170 if (!fieldConversions.isEmpty()) {
171 quickfinderActionField.addActionParameter(UifParameters.CONVERSION_FIELDS,
172 KRADUtils.buildMapParameterString(fieldConversions));
173 }
174
175 if (!lookupParameters.isEmpty()) {
176 quickfinderActionField.addActionParameter(UifParameters.LOOKUP_PARAMETERS,
177 KRADUtils.buildMapParameterString(lookupParameters));
178 }
179
180 addActionParameterIfNotNull(UifParameters.VIEW_NAME, viewName);
181 addActionParameterIfNotNull(UifParameters.READ_ONLY_FIELDS, readOnlySearchFields);
182 addActionParameterIfNotNull(UifParameters.HIDE_RETURN_LINK, hideReturnLink);
183 addActionParameterIfNotNull(UifParameters.SUPRESS_ACTIONS, suppressActions);
184 addActionParameterIfNotNull(UifParameters.REFERENCES_TO_REFRESH, referencesToRefresh);
185 addActionParameterIfNotNull(UifParameters.AUTO_SEARCH, autoSearch);
186 addActionParameterIfNotNull(UifParameters.LOOKUP_CRITERIA_ENABLED, lookupCriteriaEnabled);
187 addActionParameterIfNotNull(UifParameters.SUPPLEMENTAL_ACTIONS_ENABLED, supplementalActionsEnabled);
188 addActionParameterIfNotNull(UifParameters.DISABLE_SEARCH_BUTTONS, disableSearchButtons);
189 addActionParameterIfNotNull(UifParameters.HEADER_BAR_ENABLED, headerBarEnabled);
190 addActionParameterIfNotNull(UifParameters.SHOW_MAINTENANCE_LINKS, showMaintenanceLinks);
191 addActionParameterIfNotNull(UifParameters.MULTIPLE_VALUES_SELECT, multipleValuesSelect);
192 addActionParameterIfNotNull(UifParameters.LOOKUP_COLLECTION_NAME, lookupCollectionName);
193
194 // TODO:
195 // org.kuali.rice.kns.util.FieldUtils.populateQuickfinderDefaultsForLookup(Class,
196 // String, Field)
197 }
198
199 protected void addActionParameterIfNotNull(String parameterName, Object parameterValue) {
200 if ((parameterValue != null) && StringUtils.isNotBlank(parameterValue.toString())) {
201 quickfinderActionField.addActionParameter(parameterName, parameterValue.toString());
202 }
203 }
204
205 protected DataObjectRelationship getRelationshipForField(View view, Object model, InputField field) {
206 String propertyName = field.getBindingInfo().getBindingName();
207
208 // get object instance and class for parent
209 Object parentObject = ViewModelUtils.getParentObjectForMetadata(view, model, field);
210 Class<?> parentObjectClass = null;
211 if (parentObject != null) {
212 parentObjectClass = parentObject.getClass();
213 }
214
215 // get relationship from metadata service
216 return KRADServiceLocatorWeb.getDataObjectMetaDataService().getDataObjectRelationship(parentObject,
217 parentObjectClass, propertyName, "", true, true, false);
218 }
219
220 protected void generateFieldConversions(InputField field, DataObjectRelationship relationship) {
221 fieldConversions = new HashMap<String, String>();
222 for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) {
223 String fromField = entry.getValue();
224 String toField = entry.getKey();
225
226 // TODO: displayedFieldnames in
227 // org.kuali.rice.kns.lookup.LookupUtils.generateFieldConversions(BusinessObject,
228 // String, DataObjectRelationship, String, List, String)
229
230 fieldConversions.put(fromField, toField);
231 }
232 }
233
234 protected void generateLookupParameters(InputField field, DataObjectRelationship relationship) {
235 lookupParameters = new HashMap<String, String>();
236 for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) {
237 String fromField = entry.getKey();
238 String toField = entry.getValue();
239
240 // TODO: displayedFieldnames and displayedQFFieldNames in
241 // generateLookupParameters(BusinessObject,
242 // String, DataObjectRelationship, String, List, String)
243
244 if (relationship.getUserVisibleIdentifierKey() == null || relationship.getUserVisibleIdentifierKey().equals(
245 fromField)) {
246 lookupParameters.put(fromField, toField);
247 }
248 }
249 }
250
251 /**
252 * Adjusts the path on the field conversion to property to match the binding
253 * path prefix of the given <code>BindingInfo</code>
254 *
255 * @param bindingInfo - binding info instance to copy binding path prefix from
256 */
257 public void updateFieldConversions(BindingInfo bindingInfo) {
258 Map<String, String> adjustedFieldConversions = new HashMap<String, String>();
259 for (String fromField : fieldConversions.keySet()) {
260 String toField = fieldConversions.get(fromField);
261 String adjustedToFieldPath = bindingInfo.getPropertyAdjustedBindingPath(toField);
262
263 adjustedFieldConversions.put(fromField, adjustedToFieldPath);
264 }
265
266 this.fieldConversions = adjustedFieldConversions;
267 }
268
269 /**
270 * Adjusts the path on the lookup parameter from property to match the binding
271 * path prefix of the given <code>BindingInfo</code>
272 *
273 * @param bindingInfo - binding info instance to copy binding path prefix from
274 */
275 public void updateLookupParameters(BindingInfo bindingInfo) {
276 Map<String, String> adjustedLookupParameters = new HashMap<String, String>();
277 for (String fromField : lookupParameters.keySet()) {
278 String toField = lookupParameters.get(fromField);
279 String adjustedFromFieldPath = bindingInfo.getPropertyAdjustedBindingPath(fromField);
280
281 adjustedLookupParameters.put(adjustedFromFieldPath, toField);
282 }
283
284 this.lookupParameters = adjustedLookupParameters;
285 }
286
287 /**
288 * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
289 */
290 @Override
291 public List<Component> getComponentsForLifecycle() {
292 List<Component> components = super.getComponentsForLifecycle();
293
294 components.add(quickfinderActionField);
295
296 return components;
297 }
298
299 public String getBaseLookupUrl() {
300 return this.baseLookupUrl;
301 }
302
303 public void setBaseLookupUrl(String baseLookupUrl) {
304 this.baseLookupUrl = baseLookupUrl;
305 }
306
307 public String getDataObjectClassName() {
308 return this.dataObjectClassName;
309 }
310
311 public void setDataObjectClassName(String dataObjectClassName) {
312 this.dataObjectClassName = dataObjectClassName;
313 }
314
315 public String getViewName() {
316 return this.viewName;
317 }
318
319 public void setViewName(String viewName) {
320 this.viewName = viewName;
321 }
322
323 public String getReferencesToRefresh() {
324 return this.referencesToRefresh;
325 }
326
327 public void setReferencesToRefresh(String referencesToRefresh) {
328 this.referencesToRefresh = referencesToRefresh;
329 }
330
331 public Map<String, String> getFieldConversions() {
332 return this.fieldConversions;
333 }
334
335 public void setFieldConversions(Map<String, String> fieldConversions) {
336 this.fieldConversions = fieldConversions;
337 }
338
339 public Map<String, String> getLookupParameters() {
340 return this.lookupParameters;
341 }
342
343 public void setLookupParameters(Map<String, String> lookupParameters) {
344 this.lookupParameters = lookupParameters;
345 }
346
347 public String getReadOnlySearchFields() {
348 return this.readOnlySearchFields;
349 }
350
351 public void setReadOnlySearchFields(String readOnlySearchFields) {
352 this.readOnlySearchFields = readOnlySearchFields;
353 }
354
355 public Boolean getHideReturnLink() {
356 return this.hideReturnLink;
357 }
358
359 public void setHideReturnLink(Boolean hideReturnLink) {
360 this.hideReturnLink = hideReturnLink;
361 }
362
363 public Boolean getSuppressActions() {
364 return suppressActions;
365 }
366
367 public void setSuppressActions(Boolean suppressActions) {
368 this.suppressActions = suppressActions;
369 }
370
371 public Boolean getAutoSearch() {
372 return this.autoSearch;
373 }
374
375 public void setAutoSearch(Boolean autoSearch) {
376 this.autoSearch = autoSearch;
377 }
378
379 public Boolean getLookupCriteriaEnabled() {
380 return this.lookupCriteriaEnabled;
381 }
382
383 public void setLookupCriteriaEnabled(Boolean lookupCriteriaEnabled) {
384 this.lookupCriteriaEnabled = lookupCriteriaEnabled;
385 }
386
387 public Boolean getSupplementalActionsEnabled() {
388 return this.supplementalActionsEnabled;
389 }
390
391 public void setSupplementalActionsEnabled(Boolean supplementalActionsEnabled) {
392 this.supplementalActionsEnabled = supplementalActionsEnabled;
393 }
394
395 public Boolean getDisableSearchButtons() {
396 return this.disableSearchButtons;
397 }
398
399 public void setDisableSearchButtons(Boolean disableSearchButtons) {
400 this.disableSearchButtons = disableSearchButtons;
401 }
402
403 public Boolean getHeaderBarEnabled() {
404 return this.headerBarEnabled;
405 }
406
407 public void setHeaderBarEnabled(Boolean headerBarEnabled) {
408 this.headerBarEnabled = headerBarEnabled;
409 }
410
411 public Boolean getShowMaintenanceLinks() {
412 return this.showMaintenanceLinks;
413 }
414
415 public void setShowMaintenanceLinks(Boolean showMaintenanceLinks) {
416 this.showMaintenanceLinks = showMaintenanceLinks;
417 }
418
419 public ActionField getQuickfinderActionField() {
420 return this.quickfinderActionField;
421 }
422
423 public void setQuickfinderActionField(ActionField quickfinderActionField) {
424 this.quickfinderActionField = quickfinderActionField;
425 }
426
427 /**
428 * Indicates whether a multi-values lookup should be requested
429 *
430 * @return boolean true if multi-value lookup should be requested, false for normal lookup
431 */
432 public Boolean getMultipleValuesSelect() {
433 return multipleValuesSelect;
434 }
435
436 /**
437 * Setter for the multi-values lookup indicator
438 *
439 * @param multipleValuesSelect
440 */
441 public void setMultipleValuesSelect(Boolean multipleValuesSelect) {
442 this.multipleValuesSelect = multipleValuesSelect;
443 }
444
445 /**
446 * For the case of multi-value lookup, indicates the collection that should be populated with
447 * the return results
448 *
449 * <p>
450 * Note when the quickfinder is associated with a <code>CollectionGroup</code>, this property is
451 * set automatically from the collection name associated with the group
452 * </p>
453 *
454 * @return String collection name (must be full binding path)
455 */
456 public String getLookupCollectionName() {
457 return lookupCollectionName;
458 }
459
460 /**
461 * Setter for the name of the collection that should be populated with lookup results
462 *
463 * @param lookupCollectionName
464 */
465 public void setLookupCollectionName(String lookupCollectionName) {
466 this.lookupCollectionName = lookupCollectionName;
467 }
468 }