View Javadoc

1   /**
2    * Copyright 2005-2012 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.component;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
20  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
21  import org.kuali.rice.krad.datadictionary.uif.UifDictionaryBeanBase;
22  import org.kuali.rice.krad.uif.UifConstants;
23  import org.kuali.rice.krad.uif.view.View;
24  import org.kuali.rice.krad.util.ObjectUtils;
25  
26  import java.io.Serializable;
27  
28  /**
29   * Provides binding configuration for an DataBinding component (attribute or
30   * collection)
31   *
32   * <p>
33   * From the binding configuration the binding path is determined (if not
34   * manually set) and used to set the path in the UI or to get the value from the
35   * model
36   * </p>
37   *
38   * @author Kuali Rice Team (rice.collab@kuali.org)
39   */
40  @BeanTag(name = "bindingInfo", parent = "Uif-BindingInfo")
41  public class BindingInfo extends UifDictionaryBeanBase implements Serializable {
42      private static final long serialVersionUID = -7389398061672136091L;
43  
44      private boolean bindToForm;
45      private boolean bindToMap;
46  
47      private String bindingName;
48      private String bindByNamePrefix;
49      private String bindingObjectPath;
50  
51      private String collectionPath;
52  
53      private String bindingPath;
54  
55      public BindingInfo() {
56          super();
57  
58          bindToForm = false;
59          bindToMap = false;
60      }
61  
62      /**
63       * Sets up some default binding properties based on the view configuration
64       * and the component's property name
65       *
66       * <p>
67       * Sets the bindingName (if not set) to the given property name, and if the
68       * binding object path has not been set uses the default binding object path
69       * setup for the view
70       * </p>
71       *
72       * @param view
73       *            - the view instance the component belongs to
74       * @param propertyName
75       *            - name of the property (relative to the parent object) the
76       *            component binds to
77       */
78      public void setDefaults(View view, String propertyName) {
79          if (StringUtils.isBlank(bindingName)) {
80              bindingName = propertyName;
81          }
82  
83          if (StringUtils.isBlank(bindingObjectPath)) {
84              bindingObjectPath = view.getDefaultBindingObjectPath();
85          }
86      }
87  
88      /**
89       * Path to the property on the model the component binds to. Uses standard
90       * dot notation for nested properties. If the binding path was manually set
91       * it will be returned as it is, otherwise the path will be formed by using
92       * the binding object path and the bind prefix
93       *
94       * <p>
95       * e.g. Property name 'foo' on a model would have binding path "foo", while
96       * property name 'name' of the nested model property 'account' would have
97       * binding path "account.name"
98       * </p>
99       *
100      * @return String binding path
101      */
102     @BeanTagAttribute(name="bindingPath")
103     public String getBindingPath() {
104         if (StringUtils.isNotBlank(bindingPath)) {
105             return bindingPath;
106         }
107 
108         String formedBindingPath = "";
109 
110         if (!bindToForm && StringUtils.isNotBlank(bindingObjectPath)) {
111             formedBindingPath = bindingObjectPath;
112         }
113 
114         if (StringUtils.isNotBlank(bindByNamePrefix)) {
115             if (!bindByNamePrefix.startsWith("[") && StringUtils.isNotBlank(formedBindingPath)) {
116                 formedBindingPath += ".";
117             }
118             formedBindingPath += bindByNamePrefix;
119         }
120 
121         if (bindToMap) {
122             formedBindingPath += "[" + bindingName + "]";
123         } else {
124             if (StringUtils.isNotBlank(formedBindingPath)) {
125                 formedBindingPath += ".";
126             }
127             formedBindingPath += bindingName;
128         }
129 
130         return formedBindingPath;
131     }
132 
133     /**
134      * Returns the binding prefix string that can be used to setup the binding
135      * on <code>DataBinding</code> components that are children of the component
136      * that contains the <code>BindingInfo</code>. The binding prefix is formed
137      * like the binding path but without including the object path
138      *
139      * @return String binding prefix for nested components
140      */
141     public String getBindingPrefixForNested() {
142         String bindingPrefix = "";
143 
144         if (StringUtils.isNotBlank(bindByNamePrefix)) {
145             bindingPrefix = bindByNamePrefix;
146         }
147 
148         if (bindToMap) {
149             bindingPrefix += "['" + bindingName + "']";
150         } else {
151             if (StringUtils.isNotBlank(bindingPrefix)) {
152                 bindingPrefix += ".";
153             }
154             bindingPrefix += bindingName;
155         }
156 
157         return bindingPrefix;
158     }
159 
160     /**
161      * Returns the binding path that is formed by taking the binding configuration
162      * of this <code>BindingInfo</code> instance with the given property path as the
163      * binding name. This can be used to get the binding path when just a property
164      * name is given that is assumed to be on the same parent object of the field with
165      * the configured binding info
166      *
167      * <p>
168      * Special check is done for org.kuali.rice.krad.uif.UifConstants#NO_BIND_ADJUST_PREFIX prefix
169      * on the property name which indicates the property path is the full path and should
170      * not be adjusted. Also, if the property is prefixed with
171      * org.kuali.rice.krad.uif.UifConstants#FIELD_PATH_BIND_ADJUST_PREFIX, this indicates we should only append the
172      * binding object path
173      * </p>
174      *
175      * @param propertyPath - path for property to return full binding path for
176      * @return String full binding path
177      */
178     public String getPropertyAdjustedBindingPath(String propertyPath) {
179         if (propertyPath.startsWith(UifConstants.NO_BIND_ADJUST_PREFIX)) {
180             propertyPath = StringUtils.removeStart(propertyPath, UifConstants.NO_BIND_ADJUST_PREFIX);
181             return propertyPath;
182         }
183 
184         BindingInfo bindingInfoCopy = (BindingInfo) ObjectUtils.deepCopy(this);
185 
186         // clear the path if explicitly set
187         bindingInfoCopy.setBindingPath("");
188 
189         if (propertyPath.startsWith(UifConstants.FIELD_PATH_BIND_ADJUST_PREFIX)) {
190             bindingInfoCopy.setBindByNamePrefix("");
191             propertyPath = StringUtils.removeStart(propertyPath, UifConstants.FIELD_PATH_BIND_ADJUST_PREFIX);
192         }
193         bindingInfoCopy.setBindingName(propertyPath);
194 
195         return bindingInfoCopy.getBindingPath();
196     }
197 
198     /**
199      * Helper method for adding a path to the binding prefix
200      *
201      * @param bindPrefix - path to add
202      */
203     public void addToBindByNamePrefix(String bindPrefix) {
204         if (StringUtils.isNotBlank(bindByNamePrefix) && StringUtils.isNotBlank(bindPrefix)) {
205             bindByNamePrefix += "." + bindPrefix;
206         } else {
207             bindByNamePrefix = bindPrefix;
208         }
209     }
210 
211     /**
212      * Setter for the binding path. Can be left blank in which the path will be
213      * determined from the binding configuration
214      *
215      * @param bindingPath
216      */
217     public void setBindingPath(String bindingPath) {
218         this.bindingPath = bindingPath;
219     }
220 
221     /**
222      * Indicates whether the component binds directly to the form (that is its
223      * bindingName gives a property available through the form), or whether is
224      * binds through a nested form object. If bindToForm is false, it is assumed
225      * the component binds to the object given by the form property whose path
226      * is configured by bindingObjectPath.
227      *
228      * @return boolean true if component binds directly to form, false if it
229      *         binds to a nested object
230      */
231     @BeanTagAttribute(name="bindToForm")
232     public boolean isBindToForm() {
233         return this.bindToForm;
234     }
235 
236     /**
237      * Setter for the bind to form indicator
238      *
239      * @param bindToForm
240      */
241     public void setBindToForm(boolean bindToForm) {
242         this.bindToForm = bindToForm;
243     }
244 
245     /**
246      * Gives the name of the property that the component binds to. The name can
247      * be nested but not the full path, just from the parent object or in the
248      * case of binding directly to the form from the form object
249      *
250      * <p>
251      * If blank this will be set from the name field of the component
252      * </p>
253      *
254      * @return String name of the bind property
255      */
256     @BeanTagAttribute(name="bindingName")
257     public String getBindingName() {
258         return this.bindingName;
259     }
260 
261     /**
262      * Setter for the bind property name
263      *
264      * @param bindingName
265      */
266     public void setBindingName(String bindingName) {
267         this.bindingName = bindingName;
268     }
269 
270     /**
271      * Prefix that will be used to form the binding path from the component
272      * name. Typically used for nested collection properties
273      *
274      * @return String binding prefix
275      */
276     @BeanTagAttribute(name="bindByNamePrefix")
277     public String getBindByNamePrefix() {
278         return this.bindByNamePrefix;
279     }
280 
281     /**
282      * Setter for the prefix to use for forming the binding path by name
283      *
284      * @param bindByNamePrefix
285      */
286     public void setBindByNamePrefix(String bindByNamePrefix) {
287         this.bindByNamePrefix = bindByNamePrefix;
288     }
289 
290     /**
291      * If field is part of a collection field, gives path to collection
292      *
293      * <p>
294      * This is used for metadata purposes when getting finding the attribute
295      * definition from the dictionary and is not used in building the final
296      * binding path
297      * </p>
298      *
299      * @return String path to collection
300      */
301     public String getCollectionPath() {
302         return this.collectionPath;
303     }
304 
305     /**
306      * Setter for the field's collection path (if part of a collection)
307      *
308      * @param collectionPath
309      */
310     public void setCollectionPath(String collectionPath) {
311         this.collectionPath = collectionPath;
312     }
313 
314     /**
315      * For attribute fields that do not belong to the default form object (given
316      * by the view), this field specifies the path to the object (on the form)
317      * the attribute does belong to.
318      *
319      * <p>
320      * e.g. Say we have an attribute field with property name 'number', that
321      * belongs to the object given by the 'account' property on the form. The
322      * form object path would therefore be set to 'account'. If the property
323      * belonged to the object given by the 'document.header' property of the
324      * form, the binding object path would be set to 'document.header'. Note if
325      * the binding object path is not set for an attribute field (or any
326      * <code>DataBinding</code> component), the binding object path configured
327      * on the <code>View</code> will be used (unless bindToForm is set to true,
328      * where is assumed the property is directly available from the form).
329      * </p>
330      *
331      * @return String path to object from form
332      */
333     @BeanTagAttribute(name="bindingObjectPath")
334     public String getBindingObjectPath() {
335         return this.bindingObjectPath;
336     }
337 
338     /**
339      * Setter for the object path on the form
340      *
341      * @param bindingObjectPath
342      */
343     public void setBindingObjectPath(String bindingObjectPath) {
344         this.bindingObjectPath = bindingObjectPath;
345     }
346 
347     /**
348      * Indicates whether the parent object for the property that we are binding
349      * to is a Map. If true the binding path will be adjusted to use the map key
350      * syntax
351      *
352      * @return boolean true if the property binds to a map, false if it does not
353      */
354     @BeanTagAttribute(name="bindToMap")
355     public boolean isBindToMap() {
356         return this.bindToMap;
357     }
358 
359     /**
360      * Setter for the bind to map indicator
361      *
362      * @param bindToMap
363      */
364     public void setBindToMap(boolean bindToMap) {
365         this.bindToMap = bindToMap;
366     }
367 
368 }