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