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 }