View Javadoc

1   /**
2    * Copyright 2005-2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.widget;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.krad.bo.DataObjectRelationship;
20  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
21  import org.kuali.rice.krad.uif.UifParameters;
22  import org.kuali.rice.krad.uif.container.CollectionGroup;
23  import org.kuali.rice.krad.uif.field.InputField;
24  import org.kuali.rice.krad.uif.view.View;
25  import org.kuali.rice.krad.uif.component.BindingInfo;
26  import org.kuali.rice.krad.uif.component.Component;
27  import org.kuali.rice.krad.uif.field.ActionField;
28  import org.kuali.rice.krad.uif.util.ViewModelUtils;
29  import org.kuali.rice.krad.util.KRADUtils;
30  
31  import java.util.HashMap;
32  import java.util.List;
33  import java.util.Map;
34  
35  /**
36   * Widget for navigating to a lookup from a field (called a quickfinder)
37   *
38   * @author Kuali Rice Team (rice.collab@kuali.org)
39   */
40  public class QuickFinder extends WidgetBase {
41      private static final long serialVersionUID = 3302390972815386785L;
42  
43      // lookup configuration
44      private String baseLookupUrl;
45      private String dataObjectClassName;
46      private String viewName;
47  
48      private String referencesToRefresh;
49  
50      private Map<String, String> fieldConversions;
51      private Map<String, String> lookupParameters;
52  
53      // lookup view options
54      private String readOnlySearchFields;
55  
56      private Boolean hideReturnLink;
57      private Boolean suppressActions;
58      private Boolean autoSearch;
59      private Boolean lookupCriteriaEnabled;
60      private Boolean supplementalActionsEnabled;
61      private Boolean disableSearchButtons;
62      private Boolean headerBarEnabled;
63      private Boolean showMaintenanceLinks;
64  
65      private Boolean multipleValuesSelect;
66      private String lookupCollectionName;
67  
68      private ActionField quickfinderActionField;
69  
70      public QuickFinder() {
71          super();
72  
73          fieldConversions = new HashMap<String, String>();
74          lookupParameters = new HashMap<String, String>();
75      }
76  
77      /**
78       * The following finalization is performed:
79       *
80       * <ul>
81       * <li>
82       * Sets defaults on collectionLookup such as collectionName, and the class if not set
83       *
84       * <p>
85       * If the data object class was not configured for the lookup, the class configured for the collection group will
86       * be used if it has a lookup defined. If not data object class is found for the lookup it will be disabled. The
87       * collection name is also defaulted to the binding path for this collection group, so the results returned from
88       * the lookup will populate this collection. Finally field conversions will be generated based on the PK fields of
89       * the collection object class
90       * </p>
91       * </li>
92       * </ul>
93       *
94       * @see org.kuali.rice.krad.uif.widget.Widget#performFinalize(org.kuali.rice.krad.uif.view.View,
95       *      java.lang.Object, org.kuali.rice.krad.uif.component.Component)
96       */
97      @Override
98      public void performFinalize(View view, Object model, Component parent) {
99          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 }