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 }