1 /* 2 * Copyright 2002-2011 the original author or authors. 3 * 4 * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0 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 17 package org.springframework.validation; 18 19 import java.beans.PropertyEditor; 20 import java.util.HashMap; 21 import java.util.Map; 22 23 import org.apache.commons.logging.Log; 24 import org.apache.commons.logging.LogFactory; 25 26 import org.springframework.beans.ConfigurablePropertyAccessor; 27 import org.springframework.beans.MutablePropertyValues; 28 import org.springframework.beans.PropertyAccessException; 29 import org.springframework.beans.PropertyAccessorUtils; 30 import org.springframework.beans.PropertyBatchUpdateException; 31 import org.springframework.beans.PropertyEditorRegistry; 32 import org.springframework.beans.PropertyValue; 33 import org.springframework.beans.PropertyValues; 34 import org.springframework.beans.SimpleTypeConverter; 35 import org.springframework.beans.TypeConverter; 36 import org.springframework.beans.TypeMismatchException; 37 import org.springframework.core.MethodParameter; 38 import org.springframework.core.convert.ConversionService; 39 import org.springframework.util.Assert; 40 import org.springframework.util.ObjectUtils; 41 import org.springframework.util.PatternMatchUtils; 42 import org.springframework.util.StringUtils; 43 44 /** 45 * Binder that allows for setting property values onto a target object, 46 * including support for validation and binding result analysis. 47 * The binding process can be customized through specifying allowed fields, 48 * required fields, custom editors, etc. 49 * 50 * <p>Note that there are potential security implications in failing to set an array 51 * of allowed fields. In the case of HTTP form POST data for example, malicious clients 52 * can attempt to subvert an application by supplying values for fields or properties 53 * that do not exist on the form. In some cases this could lead to illegal data being 54 * set on command objects <i>or their nested objects</i>. For this reason, it is 55 * <b>highly recommended to specify the {@link #setAllowedFields allowedFields} property</b> 56 * on the DataBinder. 57 * 58 * <p>The binding results can be examined via the {@link BindingResult} interface, 59 * extending the {@link Errors} interface: see the {@link #getBindingResult()} method. 60 * Missing fields and property access exceptions will be converted to {@link FieldError FieldErrors}, 61 * collected in the Errors instance, using the following error codes: 62 * 63 * <ul> 64 * <li>Missing field error: "required" 65 * <li>Type mismatch error: "typeMismatch" 66 * <li>Method invocation error: "methodInvocation" 67 * </ul> 68 * 69 * <p>By default, binding errors get resolved through the {@link BindingErrorProcessor} 70 * strategy, processing for missing fields and property access exceptions: see the 71 * {@link #setBindingErrorProcessor} method. You can override the default strategy 72 * if needed, for example to generate different error codes. 73 * 74 * <p>Custom validation errors can be added afterwards. You will typically want to resolve 75 * such error codes into proper user-visible error messages; this can be achieved through 76 * resolving each error via a {@link org.springframework.context.MessageSource}, which is 77 * able to resolve an {@link ObjectError}/{@link FieldError} through its 78 * {@link org.springframework.context.MessageSource#getMessage(org.springframework.context.MessageSourceResolvable, java.util.Locale)} 79 * method. The list of message codes can be customized through the {@link MessageCodesResolver} 80 * strategy: see the {@link #setMessageCodesResolver} method. {@link DefaultMessageCodesResolver}'s 81 * javadoc states details on the default resolution rules. 82 * 83 * <p>This generic data binder can be used in any kind of environment. 84 * It is typically used by Spring web MVC controllers, via the web-specific 85 * subclasses {@link org.springframework.web.bind.ServletRequestDataBinder} 86 * and {@link org.springframework.web.portlet.bind.PortletRequestDataBinder}. 87 * 88 * @author Rod Johnson 89 * @author Juergen Hoeller 90 * @author Rob Harrop 91 * @see #setAllowedFields 92 * @see #setRequiredFields 93 * @see #registerCustomEditor 94 * @see #setMessageCodesResolver 95 * @see #setBindingErrorProcessor 96 * @see #bind 97 * @see #getBindingResult 98 * @see DefaultMessageCodesResolver 99 * @see DefaultBindingErrorProcessor 100 * @see org.springframework.context.MessageSource 101 * @see org.springframework.web.bind.ServletRequestDataBinder 102 */ 103 public class DataBinder implements PropertyEditorRegistry, TypeConverter { 104 105 /** Default object name used for binding: "target" */ 106 public static final String DEFAULT_OBJECT_NAME = "target"; 107 108 /** Default limit for array and collection growing: 256 */ 109 public static final int DEFAULT_AUTO_GROW_COLLECTION_LIMIT = 256; 110 111 112 /** 113 * We'll create a lot of DataBinder instances: Let's use a static logger. 114 */ 115 protected static final Log logger = LogFactory.getLog(DataBinder.class); 116 117 private final Object target; 118 119 private final String objectName; 120 121 private AbstractPropertyBindingResult bindingResult; 122 123 private SimpleTypeConverter typeConverter; 124 125 private BindException bindException; 126 127 private boolean ignoreUnknownFields = true; 128 129 private boolean ignoreInvalidFields = false; 130 131 private boolean autoGrowNestedPaths = true; 132 133 private int autoGrowCollectionLimit = DEFAULT_AUTO_GROW_COLLECTION_LIMIT; 134 135 private String[] allowedFields; 136 137 private String[] disallowedFields; 138 139 private String[] requiredFields; 140 141 private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor(); 142 143 private Validator validator; 144 145 private ConversionService conversionService; 146 147 148 /** 149 * Create a new DataBinder instance, with default object name. 150 * @param target the target object to bind onto (or <code>null</code> 151 * if the binder is just used to convert a plain parameter value) 152 * @see #DEFAULT_OBJECT_NAME 153 */ DataBinder(Object target)154 public DataBinder(Object target) { 155 this(target, DEFAULT_OBJECT_NAME); 156 } 157 158 /** 159 * Create a new DataBinder instance. 160 * @param target the target object to bind onto (or <code>null</code> 161 * if the binder is just used to convert a plain parameter value) 162 * @param objectName the name of the target object 163 */ DataBinder(Object target, String objectName)164 public DataBinder(Object target, String objectName) { 165 this.target = target; 166 this.objectName = objectName; 167 } 168 169 170 /** 171 * Return the wrapped target object. 172 */ getTarget()173 public Object getTarget() { 174 return this.target; 175 } 176 177 /** 178 * Return the name of the bound object. 179 */ getObjectName()180 public String getObjectName() { 181 return this.objectName; 182 } 183 184 /** 185 * Set whether this binder should attempt to "auto-grow" a nested path that contains a null value. 186 * <p>If "true", a null path location will be populated with a default object value and traversed 187 * instead of resulting in an exception. This flag also enables auto-growth of collection elements 188 * when accessing an out-of-bounds index. 189 * <p>Default is "true" on a standard DataBinder. Note that this feature is only supported 190 * for bean property access (DataBinder's default mode), not for field access. 191 * @see #initBeanPropertyAccess() 192 * @see org.springframework.beans.BeanWrapper#setAutoGrowNestedPaths 193 */ setAutoGrowNestedPaths(boolean autoGrowNestedPaths)194 public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) { 195 Assert.state(this.bindingResult == null, 196 "DataBinder is already initialized - call setAutoGrowNestedPaths before other configuration methods"); 197 this.autoGrowNestedPaths = autoGrowNestedPaths; 198 } 199 200 /** 201 * Return whether "auto-growing" of nested paths has been activated. 202 */ isAutoGrowNestedPaths()203 public boolean isAutoGrowNestedPaths() { 204 return this.autoGrowNestedPaths; 205 } 206 207 /** 208 * Specify the limit for array and collection auto-growing. 209 * <p>Default is 256, preventing OutOfMemoryErrors in case of large indexes. 210 * Raise this limit if your auto-growing needs are unusually high. 211 */ setAutoGrowCollectionLimit(int autoGrowCollectionLimit)212 public void setAutoGrowCollectionLimit(int autoGrowCollectionLimit) { 213 this.autoGrowCollectionLimit = autoGrowCollectionLimit; 214 } 215 216 /** 217 * Return the current limit for array and collection auto-growing. 218 */ getAutoGrowCollectionLimit()219 public int getAutoGrowCollectionLimit() { 220 return this.autoGrowCollectionLimit; 221 } 222 223 /** 224 * Initialize standard JavaBean property access for this DataBinder. 225 * <p>This is the default; an explicit call just leads to eager initialization. 226 * @see #initDirectFieldAccess() 227 */ initBeanPropertyAccess()228 public void initBeanPropertyAccess() { 229 Assert.state(this.bindingResult == null, 230 "DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods"); 231 this.bindingResult = new BeanPropertyBindingResult( 232 getTarget(), getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit()); 233 if (this.conversionService != null) { 234 this.bindingResult.initConversion(this.conversionService); 235 } 236 } 237 238 /** 239 * Initialize direct field access for this DataBinder, 240 * as alternative to the default bean property access. 241 * @see #initBeanPropertyAccess() 242 */ initDirectFieldAccess()243 public void initDirectFieldAccess() { 244 Assert.state(this.bindingResult == null, 245 "DataBinder is already initialized - call initDirectFieldAccess before other configuration methods"); 246 this.bindingResult = new DirectFieldBindingResult(getTarget(), getObjectName()); 247 if (this.conversionService != null) { 248 this.bindingResult.initConversion(this.conversionService); 249 } 250 } 251 252 /** 253 * Return the internal BindingResult held by this DataBinder, 254 * as AbstractPropertyBindingResult. 255 */ getInternalBindingResult()256 protected AbstractPropertyBindingResult getInternalBindingResult() { 257 if (this.bindingResult == null) { 258 initBeanPropertyAccess(); 259 } 260 return this.bindingResult; 261 } 262 263 /** 264 * Return the underlying PropertyAccessor of this binder's BindingResult. 265 */ getPropertyAccessor()266 protected ConfigurablePropertyAccessor getPropertyAccessor() { 267 return getInternalBindingResult().getPropertyAccessor(); 268 } 269 270 /** 271 * Return this binder's underlying SimpleTypeConverter. 272 */ getSimpleTypeConverter()273 protected SimpleTypeConverter getSimpleTypeConverter() { 274 if (this.typeConverter == null) { 275 this.typeConverter = new SimpleTypeConverter(); 276 if (this.conversionService != null) { 277 this.typeConverter.setConversionService(this.conversionService); 278 } 279 } 280 return this.typeConverter; 281 } 282 283 /** 284 * Return the underlying TypeConverter of this binder's BindingResult. 285 */ getPropertyEditorRegistry()286 protected PropertyEditorRegistry getPropertyEditorRegistry() { 287 if (getTarget() != null) { 288 return getInternalBindingResult().getPropertyAccessor(); 289 } 290 else { 291 return getSimpleTypeConverter(); 292 } 293 } 294 295 /** 296 * Return the underlying TypeConverter of this binder's BindingResult. 297 */ getTypeConverter()298 protected TypeConverter getTypeConverter() { 299 if (getTarget() != null) { 300 return getInternalBindingResult().getPropertyAccessor(); 301 } 302 else { 303 return getSimpleTypeConverter(); 304 } 305 } 306 307 /** 308 * Return the BindingResult instance created by this DataBinder. 309 * This allows for convenient access to the binding results after 310 * a bind operation. 311 * @return the BindingResult instance, to be treated as BindingResult 312 * or as Errors instance (Errors is a super-interface of BindingResult) 313 * @see Errors 314 * @see #bind 315 */ getBindingResult()316 public BindingResult getBindingResult() { 317 return getInternalBindingResult(); 318 } 319 320 /** 321 * Return the Errors instance for this data binder. 322 * @return the Errors instance, to be treated as Errors or as BindException 323 * @deprecated in favor of {@link #getBindingResult()}. 324 * Use the {@link BindException#BindException(BindingResult)} constructor 325 * to create a BindException instance if still needed. 326 * @see #getBindingResult() 327 */ 328 @Deprecated getErrors()329 public BindException getErrors() { 330 if (this.bindException == null) { 331 this.bindException = new BindException(getBindingResult()); 332 } 333 return this.bindException; 334 } 335 336 337 /** 338 * Set whether to ignore unknown fields, that is, whether to ignore bind 339 * parameters that do not have corresponding fields in the target object. 340 * <p>Default is "true". Turn this off to enforce that all bind parameters 341 * must have a matching field in the target object. 342 * <p>Note that this setting only applies to <i>binding</i> operations 343 * on this DataBinder, not to <i>retrieving</i> values via its 344 * {@link #getBindingResult() BindingResult}. 345 * @see #bind 346 */ setIgnoreUnknownFields(boolean ignoreUnknownFields)347 public void setIgnoreUnknownFields(boolean ignoreUnknownFields) { 348 this.ignoreUnknownFields = ignoreUnknownFields; 349 } 350 351 /** 352 * Return whether to ignore unknown fields when binding. 353 */ isIgnoreUnknownFields()354 public boolean isIgnoreUnknownFields() { 355 return this.ignoreUnknownFields; 356 } 357 358 /** 359 * Set whether to ignore invalid fields, that is, whether to ignore bind 360 * parameters that have corresponding fields in the target object which are 361 * not accessible (for example because of null values in the nested path). 362 * <p>Default is "false". Turn this on to ignore bind parameters for 363 * nested objects in non-existing parts of the target object graph. 364 * <p>Note that this setting only applies to <i>binding</i> operations 365 * on this DataBinder, not to <i>retrieving</i> values via its 366 * {@link #getBindingResult() BindingResult}. 367 * @see #bind 368 */ setIgnoreInvalidFields(boolean ignoreInvalidFields)369 public void setIgnoreInvalidFields(boolean ignoreInvalidFields) { 370 this.ignoreInvalidFields = ignoreInvalidFields; 371 } 372 373 /** 374 * Return whether to ignore invalid fields when binding. 375 */ isIgnoreInvalidFields()376 public boolean isIgnoreInvalidFields() { 377 return this.ignoreInvalidFields; 378 } 379 380 /** 381 * Register fields that should be allowed for binding. Default is all 382 * fields. Restrict this for example to avoid unwanted modifications 383 * by malicious users when binding HTTP request parameters. 384 * <p>Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching 385 * can be implemented by overriding the <code>isAllowed</code> method. 386 * <p>Alternatively, specify a list of <i>disallowed</i> fields. 387 * @param allowedFields array of field names 388 * @see #setDisallowedFields 389 * @see #isAllowed(String) 390 * @see org.springframework.web.bind.ServletRequestDataBinder 391 */ setAllowedFields(String... allowedFields)392 public void setAllowedFields(String... allowedFields) { 393 this.allowedFields = PropertyAccessorUtils.canonicalPropertyNames(allowedFields); 394 } 395 396 /** 397 * Return the fields that should be allowed for binding. 398 * @return array of field names 399 */ getAllowedFields()400 public String[] getAllowedFields() { 401 return this.allowedFields; 402 } 403 404 /** 405 * Register fields that should <i>not</i> be allowed for binding. Default is none. 406 * Mark fields as disallowed for example to avoid unwanted modifications 407 * by malicious users when binding HTTP request parameters. 408 * <p>Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching 409 * can be implemented by overriding the <code>isAllowed</code> method. 410 * <p>Alternatively, specify a list of <i>allowed</i> fields. 411 * @param disallowedFields array of field names 412 * @see #setAllowedFields 413 * @see #isAllowed(String) 414 * @see org.springframework.web.bind.ServletRequestDataBinder 415 */ setDisallowedFields(String... disallowedFields)416 public void setDisallowedFields(String... disallowedFields) { 417 this.disallowedFields = PropertyAccessorUtils.canonicalPropertyNames(disallowedFields); 418 } 419 420 /** 421 * Return the fields that should <i>not</i> be allowed for binding. 422 * @return array of field names 423 */ getDisallowedFields()424 public String[] getDisallowedFields() { 425 return this.disallowedFields; 426 } 427 428 /** 429 * Register fields that are required for each binding process. 430 * <p>If one of the specified fields is not contained in the list of 431 * incoming property values, a corresponding "missing field" error 432 * will be created, with error code "required" (by the default 433 * binding error processor). 434 * @param requiredFields array of field names 435 * @see #setBindingErrorProcessor 436 * @see DefaultBindingErrorProcessor#MISSING_FIELD_ERROR_CODE 437 */ setRequiredFields(String... requiredFields)438 public void setRequiredFields(String... requiredFields) { 439 this.requiredFields = PropertyAccessorUtils.canonicalPropertyNames(requiredFields); 440 if (logger.isDebugEnabled()) { 441 logger.debug("DataBinder requires binding of required fields [" + 442 StringUtils.arrayToCommaDelimitedString(requiredFields) + "]"); 443 } 444 } 445 446 /** 447 * Return the fields that are required for each binding process. 448 * @return array of field names 449 */ getRequiredFields()450 public String[] getRequiredFields() { 451 return this.requiredFields; 452 } 453 454 /** 455 * Set whether to extract the old field value when applying a 456 * property editor to a new value for a field. 457 * <p>Default is "true", exposing previous field values to custom editors. 458 * Turn this to "false" to avoid side effects caused by getters. 459 */ setExtractOldValueForEditor(boolean extractOldValueForEditor)460 public void setExtractOldValueForEditor(boolean extractOldValueForEditor) { 461 getPropertyAccessor().setExtractOldValueForEditor(extractOldValueForEditor); 462 } 463 464 /** 465 * Set the strategy to use for resolving errors into message codes. 466 * Applies the given strategy to the underlying errors holder. 467 * <p>Default is a DefaultMessageCodesResolver. 468 * @see BeanPropertyBindingResult#setMessageCodesResolver 469 * @see DefaultMessageCodesResolver 470 */ setMessageCodesResolver(MessageCodesResolver messageCodesResolver)471 public void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) { 472 getInternalBindingResult().setMessageCodesResolver(messageCodesResolver); 473 } 474 475 /** 476 * Set the strategy to use for processing binding errors, that is, 477 * required field errors and <code>PropertyAccessException</code>s. 478 * <p>Default is a DefaultBindingErrorProcessor. 479 * @see DefaultBindingErrorProcessor 480 */ setBindingErrorProcessor(BindingErrorProcessor bindingErrorProcessor)481 public void setBindingErrorProcessor(BindingErrorProcessor bindingErrorProcessor) { 482 Assert.notNull(bindingErrorProcessor, "BindingErrorProcessor must not be null"); 483 this.bindingErrorProcessor = bindingErrorProcessor; 484 } 485 486 /** 487 * Return the strategy for processing binding errors. 488 */ getBindingErrorProcessor()489 public BindingErrorProcessor getBindingErrorProcessor() { 490 return this.bindingErrorProcessor; 491 } 492 493 /** 494 * Set the Validator to apply after each binding step. 495 */ setValidator(Validator validator)496 public void setValidator(Validator validator) { 497 if (validator != null && (getTarget() != null && !validator.supports(getTarget().getClass()))) { 498 throw new IllegalStateException("Invalid target for Validator [" + validator + "]: " + getTarget()); 499 } 500 this.validator = validator; 501 } 502 503 /** 504 * Return the Validator to apply after each binding step, if any. 505 */ getValidator()506 public Validator getValidator() { 507 return this.validator; 508 } 509 510 511 //--------------------------------------------------------------------- 512 // Implementation of PropertyEditorRegistry/TypeConverter interface 513 //--------------------------------------------------------------------- 514 515 /** 516 * Specify a Spring 3.0 ConversionService to use for converting 517 * property values, as an alternative to JavaBeans PropertyEditors. 518 */ setConversionService(ConversionService conversionService)519 public void setConversionService(ConversionService conversionService) { 520 Assert.state(this.conversionService == null, "DataBinder is already initialized with ConversionService"); 521 this.conversionService = conversionService; 522 if (this.bindingResult != null && conversionService != null) { 523 this.bindingResult.initConversion(conversionService); 524 } 525 } 526 527 /** 528 * Return the associated ConversionService, if any. 529 */ getConversionService()530 public ConversionService getConversionService() { 531 return this.conversionService; 532 } 533 registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor)534 public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) { 535 getPropertyEditorRegistry().registerCustomEditor(requiredType, propertyEditor); 536 } 537 registerCustomEditor(Class<?> requiredType, String field, PropertyEditor propertyEditor)538 public void registerCustomEditor(Class<?> requiredType, String field, PropertyEditor propertyEditor) { 539 getPropertyEditorRegistry().registerCustomEditor(requiredType, field, propertyEditor); 540 } 541 findCustomEditor(Class<?> requiredType, String propertyPath)542 public PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath) { 543 return getPropertyEditorRegistry().findCustomEditor(requiredType, propertyPath); 544 } 545 convertIfNecessary(Object value, Class<T> requiredType)546 public <T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException { 547 return getTypeConverter().convertIfNecessary(value, requiredType); 548 } 549 convertIfNecessary( Object value, Class<T> requiredType, MethodParameter methodParam)550 public <T> T convertIfNecessary( 551 Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException { 552 553 return getTypeConverter().convertIfNecessary(value, requiredType, methodParam); 554 } 555 556 557 /** 558 * Bind the given property values to this binder's target. 559 * <p>This call can create field errors, representing basic binding 560 * errors like a required field (code "required"), or type mismatch 561 * between value and bean property (code "typeMismatch"). 562 * <p>Note that the given PropertyValues should be a throwaway instance: 563 * For efficiency, it will be modified to just contain allowed fields if it 564 * implements the MutablePropertyValues interface; else, an internal mutable 565 * copy will be created for this purpose. Pass in a copy of the PropertyValues 566 * if you want your original instance to stay unmodified in any case. 567 * @param pvs property values to bind 568 * @see #doBind(org.springframework.beans.MutablePropertyValues) 569 */ bind(PropertyValues pvs)570 public void bind(PropertyValues pvs) { 571 MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues) ? 572 (MutablePropertyValues) pvs : new MutablePropertyValues(pvs); 573 doBind(mpvs); 574 } 575 576 /** 577 * Actual implementation of the binding process, working with the 578 * passed-in MutablePropertyValues instance. 579 * @param mpvs the property values to bind, 580 * as MutablePropertyValues instance 581 * @see #checkAllowedFields 582 * @see #checkRequiredFields 583 * @see #applyPropertyValues 584 */ doBind(MutablePropertyValues mpvs)585 protected void doBind(MutablePropertyValues mpvs) { 586 checkAllowedFields(mpvs); 587 checkRequiredFields(mpvs); 588 applyPropertyValues(mpvs); 589 } 590 591 /** 592 * Check the given property values against the allowed fields, 593 * removing values for fields that are not allowed. 594 * @param mpvs the property values to be bound (can be modified) 595 * @see #getAllowedFields 596 * @see #isAllowed(String) 597 */ checkAllowedFields(MutablePropertyValues mpvs)598 protected void checkAllowedFields(MutablePropertyValues mpvs) { 599 PropertyValue[] pvs = mpvs.getPropertyValues(); 600 for (PropertyValue pv : pvs) { 601 String field = PropertyAccessorUtils.canonicalPropertyName(pv.getName()); 602 if (!isAllowed(field)) { 603 mpvs.removePropertyValue(pv); 604 getBindingResult().recordSuppressedField(field); 605 if (logger.isDebugEnabled()) { 606 logger.debug("Field [" + field + "] has been removed from PropertyValues " + 607 "and will not be bound, because it has not been found in the list of allowed fields"); 608 } 609 } 610 } 611 } 612 613 /** 614 * Return if the given field is allowed for binding. 615 * Invoked for each passed-in property value. 616 * <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, 617 * as well as direct equality, in the specified lists of allowed fields and 618 * disallowed fields. A field matching a disallowed pattern will not be accepted 619 * even if it also happens to match a pattern in the allowed list. 620 * <p>Can be overridden in subclasses. 621 * @param field the field to check 622 * @return if the field is allowed 623 * @see #setAllowedFields 624 * @see #setDisallowedFields 625 * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String) 626 */ isAllowed(String field)627 protected boolean isAllowed(String field) { 628 String[] allowed = getAllowedFields(); 629 String[] disallowed = getDisallowedFields(); 630 return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) && 631 (ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field))); 632 } 633 634 /** 635 * Check the given property values against the required fields, 636 * generating missing field errors where appropriate. 637 * @param mpvs the property values to be bound (can be modified) 638 * @see #getRequiredFields 639 * @see #getBindingErrorProcessor 640 * @see BindingErrorProcessor#processMissingFieldError 641 */ checkRequiredFields(MutablePropertyValues mpvs)642 protected void checkRequiredFields(MutablePropertyValues mpvs) { 643 String[] requiredFields = getRequiredFields(); 644 if (!ObjectUtils.isEmpty(requiredFields)) { 645 Map<String, PropertyValue> propertyValues = new HashMap<String, PropertyValue>(); 646 PropertyValue[] pvs = mpvs.getPropertyValues(); 647 for (PropertyValue pv : pvs) { 648 String canonicalName = PropertyAccessorUtils.canonicalPropertyName(pv.getName()); 649 propertyValues.put(canonicalName, pv); 650 } 651 for (String field : requiredFields) { 652 PropertyValue pv = propertyValues.get(field); 653 boolean empty = (pv == null || pv.getValue() == null); 654 if (!empty) { 655 if (pv.getValue() instanceof String) { 656 empty = !StringUtils.hasText((String) pv.getValue()); 657 } 658 else if (pv.getValue() instanceof String[]) { 659 String[] values = (String[]) pv.getValue(); 660 empty = (values.length == 0 || !StringUtils.hasText(values[0])); 661 } 662 } 663 if (empty) { 664 // Use bind error processor to create FieldError. 665 getBindingErrorProcessor().processMissingFieldError(field, getInternalBindingResult()); 666 // Remove property from property values to bind: 667 // It has already caused a field error with a rejected value. 668 if (pv != null) { 669 mpvs.removePropertyValue(pv); 670 propertyValues.remove(field); 671 } 672 } 673 } 674 } 675 } 676 677 /** 678 * Apply given property values to the target object. 679 * <p>Default implementation applies all of the supplied property 680 * values as bean property values. By default, unknown fields will 681 * be ignored. 682 * @param mpvs the property values to be bound (can be modified) 683 * @see #getTarget 684 * @see #getPropertyAccessor 685 * @see #isIgnoreUnknownFields 686 * @see #getBindingErrorProcessor 687 * @see BindingErrorProcessor#processPropertyAccessException 688 */ applyPropertyValues(MutablePropertyValues mpvs)689 protected void applyPropertyValues(MutablePropertyValues mpvs) { 690 try { 691 // Bind request parameters onto target object. 692 getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields()); 693 } 694 catch (PropertyBatchUpdateException ex) { 695 // Use bind error processor to create FieldErrors. 696 for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) { 697 getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult()); 698 } 699 } 700 } 701 702 703 /** 704 * Invoke the specified Validator, if any. 705 * @see #setValidator(Validator) 706 * @see #getBindingResult() 707 */ validate()708 public void validate() { 709 this.validator.validate(getTarget(), getBindingResult()); 710 } 711 712 /** 713 * Invoke the specified Validator, if any, with the given validation hints. 714 * <p>Note: Validation hints may get ignored by the actual target Validator. 715 * @param validationHints one or more hint objects to be passed to a {@link SmartValidator} 716 * @see #setValidator(Validator) 717 * @see SmartValidator#validate(Object, Errors, Object...) 718 */ validate(Object... validationHints)719 public void validate(Object... validationHints) { 720 Validator validator = getValidator(); 721 if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) { 722 ((SmartValidator) validator).validate(getTarget(), getBindingResult(), validationHints); 723 } 724 else if (validator != null) { 725 validator.validate(getTarget(), getBindingResult()); 726 } 727 } 728 729 /** 730 * Close this DataBinder, which may result in throwing 731 * a BindException if it encountered any errors. 732 * @return the model Map, containing target object and Errors instance 733 * @throws BindException if there were any errors in the bind operation 734 * @see BindingResult#getModel() 735 */ close()736 public Map<?, ?> close() throws BindException { 737 if (getBindingResult().hasErrors()) { 738 throw new BindException(getBindingResult()); 739 } 740 return getBindingResult().getModel(); 741 } 742 743 } 744