001 /** 002 * Copyright 2005-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.krad.uif.component; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.kuali.rice.krad.datadictionary.parse.BeanTag; 020 import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute; 021 import org.kuali.rice.krad.datadictionary.uif.UifDictionaryBeanBase; 022 import org.kuali.rice.krad.uif.UifConstants; 023 import org.kuali.rice.krad.uif.view.View; 024 import org.kuali.rice.krad.util.ObjectUtils; 025 026 import java.io.Serializable; 027 028 /** 029 * Provides binding configuration for an DataBinding component (attribute or 030 * collection) 031 * 032 * <p> 033 * From the binding configuration the binding path is determined (if not 034 * manually set) and used to set the path in the UI or to get the value from the 035 * model 036 * </p> 037 * 038 * @author Kuali Rice Team (rice.collab@kuali.org) 039 */ 040 @BeanTag(name = "bindingInfo-bean", parent = "Uif-BindingInfo") 041 public class BindingInfo extends UifDictionaryBeanBase implements Serializable { 042 private static final long serialVersionUID = -7389398061672136091L; 043 044 private boolean bindToForm; 045 private boolean bindToMap; 046 047 private String bindingName; 048 private String bindByNamePrefix; 049 private String bindingObjectPath; 050 051 private String collectionPath; 052 053 private String bindingPath; 054 055 public BindingInfo() { 056 super(); 057 058 bindToForm = false; 059 bindToMap = false; 060 } 061 062 /** 063 * Sets up some default binding properties based on the view configuration 064 * and the component's property name 065 * 066 * <p> 067 * Sets the bindingName (if not set) to the given property name, and if the 068 * binding object path has not been set uses the default binding object path 069 * setup for the view 070 * </p> 071 * 072 * @param view 073 * - the view instance the component belongs to 074 * @param propertyName 075 * - name of the property (relative to the parent object) the 076 * component binds to 077 */ 078 public void setDefaults(View view, String propertyName) { 079 if (StringUtils.isBlank(bindingName)) { 080 bindingName = propertyName; 081 } 082 083 if (StringUtils.isBlank(bindingObjectPath)) { 084 bindingObjectPath = view.getDefaultBindingObjectPath(); 085 } 086 } 087 088 /** 089 * Path to the property on the model the component binds to. Uses standard 090 * dot notation for nested properties. If the binding path was manually set 091 * it will be returned as it is, otherwise the path will be formed by using 092 * the binding object path and the bind prefix 093 * 094 * <p> 095 * e.g. Property name 'foo' on a model would have binding path "foo", while 096 * property name 'name' of the nested model property 'account' would have 097 * binding path "account.name" 098 * </p> 099 * 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 }