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 }