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