1 /** 2 * Copyright 2005-2015 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", 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 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 //KULRICE-13096 avoid duplicate property paths 179 if (propertyPath.startsWith(formedBindingPath)) { 180 return propertyPath; 181 } 182 183 if (bindToMap) { 184 formedBindingPath += "[" + propertyPath + "]"; 185 } else { 186 if (StringUtils.isNotBlank(formedBindingPath)) { 187 formedBindingPath += "."; 188 } 189 formedBindingPath += propertyPath; 190 } 191 192 return formedBindingPath; 193 } 194 195 /** 196 * Returns the binding path prefix to affix before the binding path or property adjusted binding path. 197 * 198 * @return the binding path prefix to affix before a binding path. 199 */ 200 public String getBindingPathPrefix() { 201 String bindingPathPrefix = ""; 202 203 if (!bindToForm && StringUtils.isNotBlank(bindingObjectPath)) { 204 bindingPathPrefix = bindingObjectPath; 205 } 206 207 if (StringUtils.isNotBlank(bindByNamePrefix)) { 208 if (!bindByNamePrefix.startsWith("[") && StringUtils.isNotBlank(bindingPathPrefix)) { 209 bindingPathPrefix += "."; 210 } 211 212 bindingPathPrefix += bindByNamePrefix; 213 } 214 215 return bindingPathPrefix; 216 } 217 218 /** 219 * Helper method for adding a path to the binding prefix 220 * 221 * @param bindPrefix path to add 222 */ 223 public void addToBindByNamePrefix(String bindPrefix) { 224 if (StringUtils.isNotBlank(bindByNamePrefix) && StringUtils.isNotBlank(bindPrefix)) { 225 bindByNamePrefix += "." + bindPrefix; 226 } else { 227 bindByNamePrefix = bindPrefix; 228 } 229 } 230 231 /** 232 * Setter for the binding path. Can be left blank in which the path will be 233 * determined from the binding configuration 234 * 235 * @param bindingPath 236 */ 237 public void setBindingPath(String bindingPath) { 238 this.bindingPath = bindingPath; 239 } 240 241 /** 242 * Indicates whether the component binds directly to the form (that is its 243 * bindingName gives a property available through the form), or whether is 244 * binds through a nested form object. If bindToForm is false, it is assumed 245 * the component binds to the object given by the form property whose path 246 * is configured by bindingObjectPath. 247 * 248 * @return true if component binds directly to form, false if it 249 * binds to a nested object 250 */ 251 @BeanTagAttribute 252 public boolean isBindToForm() { 253 return this.bindToForm; 254 } 255 256 /** 257 * Setter for the bind to form indicator 258 * 259 * @param bindToForm 260 */ 261 public void setBindToForm(boolean bindToForm) { 262 this.bindToForm = bindToForm; 263 } 264 265 /** 266 * Gives the name of the property that the component binds to. The name can 267 * be nested but not the full path, just from the parent object or in the 268 * case of binding directly to the form from the form object 269 * 270 * <p> 271 * If blank this will be set from the name field of the component 272 * </p> 273 * 274 * @return name of the bind property 275 */ 276 @BeanTagAttribute 277 public String getBindingName() { 278 return this.bindingName; 279 } 280 281 /** 282 * Setter for the bind property name 283 * 284 * @param bindingName 285 */ 286 public void setBindingName(String bindingName) { 287 this.bindingName = bindingName; 288 } 289 290 /** 291 * Prefix that will be used to form the binding path from the component 292 * name. Typically used for nested collection properties 293 * 294 * @return binding prefix 295 */ 296 @BeanTagAttribute 297 public String getBindByNamePrefix() { 298 return this.bindByNamePrefix; 299 } 300 301 /** 302 * Setter for the prefix to use for forming the binding path by name 303 * 304 * @param bindByNamePrefix 305 */ 306 public void setBindByNamePrefix(String bindByNamePrefix) { 307 this.bindByNamePrefix = bindByNamePrefix; 308 } 309 310 /** 311 * If field is part of a collection field, gives path to collection 312 * 313 * <p> 314 * This is used for metadata purposes when getting finding the attribute 315 * definition from the dictionary and is not used in building the final 316 * binding path 317 * </p> 318 * 319 * @return path to collection 320 */ 321 public String getCollectionPath() { 322 return this.collectionPath; 323 } 324 325 /** 326 * Setter for the field's collection path (if part of a collection) 327 * 328 * @param collectionPath 329 */ 330 public void setCollectionPath(String collectionPath) { 331 this.collectionPath = collectionPath; 332 } 333 334 /** 335 * For attribute fields that do not belong to the default form object (given 336 * by the view), this field specifies the path to the object (on the form) 337 * the attribute does belong to. 338 * 339 * <p> 340 * e.g. Say we have an attribute field with property name 'number', that 341 * belongs to the object given by the 'account' property on the form. The 342 * form object path would therefore be set to 'account'. If the property 343 * belonged to the object given by the 'document.header' property of the 344 * form, the binding object path would be set to 'document.header'. Note if 345 * the binding object path is not set for an attribute field (or any 346 * <code>DataBinding</code> component), the binding object path configured 347 * on the <code>View</code> will be used (unless bindToForm is set to true, 348 * where is assumed the property is directly available from the form). 349 * </p> 350 * 351 * @return path to object from form 352 */ 353 @BeanTagAttribute 354 public String getBindingObjectPath() { 355 return this.bindingObjectPath; 356 } 357 358 /** 359 * Setter for the object path on the form 360 * 361 * @param bindingObjectPath 362 */ 363 public void setBindingObjectPath(String bindingObjectPath) { 364 this.bindingObjectPath = bindingObjectPath; 365 } 366 367 /** 368 * Indicates whether the parent object for the property that we are binding 369 * to is a Map. If true the binding path will be adjusted to use the map key 370 * syntax 371 * 372 * @return true if the property binds to a map, false if it does not 373 */ 374 @BeanTagAttribute 375 public boolean isBindToMap() { 376 return this.bindToMap; 377 } 378 379 /** 380 * Setter for the bind to map indicator 381 * 382 * @param bindToMap 383 */ 384 public void setBindToMap(boolean bindToMap) { 385 this.bindToMap = bindToMap; 386 } 387 }