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