001/** 002 * Copyright 2005-2016 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 */ 016package org.kuali.rice.krad.uif.component; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.krad.uif.UifConstants; 020import org.kuali.rice.krad.uif.view.View; 021import org.kuali.rice.krad.util.ObjectUtils; 022 023import java.io.Serializable; 024 025/** 026 * Provides binding configuration for an DataBinding component (attribute or 027 * collection) 028 * 029 * <p> 030 * From the binding configuration the binding path is determined (if not 031 * manually set) and used to set the path in the UI or to get the value from the 032 * model 033 * </p> 034 * 035 * @author Kuali Rice Team (rice.collab@kuali.org) 036 */ 037public class BindingInfo extends ConfigurableBase implements Serializable { 038 private static final long serialVersionUID = -7389398061672136091L; 039 040 private boolean bindToForm; 041 private boolean bindToMap; 042 043 private String bindingName; 044 private String bindByNamePrefix; 045 private String bindingObjectPath; 046 047 private String collectionPath; 048 049 private String bindingPath; 050 051 public BindingInfo() { 052 super(); 053 054 bindToForm = false; 055 bindToMap = false; 056 } 057 058 /** 059 * Sets up some default binding properties based on the view configuration 060 * and the component's property name 061 * 062 * <p> 063 * Sets the bindingName (if not set) to the given property name, and if the 064 * binding object path has not been set uses the default binding object path 065 * setup for the view 066 * </p> 067 * 068 * @param view 069 * - the view instance the component belongs to 070 * @param propertyName 071 * - name of the property (relative to the parent object) the 072 * component binds to 073 */ 074 public void setDefaults(View view, String propertyName) { 075 if (StringUtils.isBlank(bindingName)) { 076 bindingName = propertyName; 077 } 078 079 if (StringUtils.isBlank(bindingObjectPath)) { 080 bindingObjectPath = view.getDefaultBindingObjectPath(); 081 } 082 } 083 084 /** 085 * Path to the property on the model the component binds to. Uses standard 086 * dot notation for nested properties. If the binding path was manually set 087 * it will be returned as it is, otherwise the path will be formed by using 088 * the binding object path and the bind prefix 089 * 090 * <p> 091 * e.g. Property name 'foo' on a model would have binding path "foo", while 092 * property name 'name' of the nested model property 'account' would have 093 * binding path "account.name" 094 * </p> 095 * 096 * @return String binding path 097 */ 098 public String getBindingPath() { 099 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#FIELD_PATH_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.FIELD_PATH_BIND_ADJUST_PREFIX)) { 185 bindingInfoCopy.setBindByNamePrefix(""); 186 propertyPath = StringUtils.removeStart(propertyPath, UifConstants.FIELD_PATH_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}