001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache 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 * https://www.apache.org/licenses/LICENSE-2.0 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 017package org.springframework.validation; 018 019import java.beans.PropertyEditor; 020import java.lang.reflect.Field; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.Optional; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031 032import org.springframework.beans.ConfigurablePropertyAccessor; 033import org.springframework.beans.MutablePropertyValues; 034import org.springframework.beans.PropertyAccessException; 035import org.springframework.beans.PropertyAccessorUtils; 036import org.springframework.beans.PropertyBatchUpdateException; 037import org.springframework.beans.PropertyEditorRegistry; 038import org.springframework.beans.PropertyValue; 039import org.springframework.beans.PropertyValues; 040import org.springframework.beans.SimpleTypeConverter; 041import org.springframework.beans.TypeConverter; 042import org.springframework.beans.TypeMismatchException; 043import org.springframework.core.MethodParameter; 044import org.springframework.core.convert.ConversionService; 045import org.springframework.format.Formatter; 046import org.springframework.format.support.FormatterPropertyEditorAdapter; 047import org.springframework.lang.UsesJava8; 048import org.springframework.util.Assert; 049import org.springframework.util.ClassUtils; 050import org.springframework.util.ObjectUtils; 051import org.springframework.util.PatternMatchUtils; 052import org.springframework.util.StringUtils; 053 054/** 055 * Binder that allows for setting property values onto a target object, 056 * including support for validation and binding result analysis. 057 * The binding process can be customized through specifying allowed fields, 058 * required fields, custom editors, etc. 059 * 060 * <p>Note that there are potential security implications in failing to set an array 061 * of allowed fields. In the case of HTTP form POST data for example, malicious clients 062 * can attempt to subvert an application by supplying values for fields or properties 063 * that do not exist on the form. In some cases this could lead to illegal data being 064 * set on command objects <i>or their nested objects</i>. For this reason, it is 065 * <b>highly recommended to specify the {@link #setAllowedFields allowedFields} property</b> 066 * on the DataBinder. 067 * 068 * <p>The binding results can be examined via the {@link BindingResult} interface, 069 * extending the {@link Errors} interface: see the {@link #getBindingResult()} method. 070 * Missing fields and property access exceptions will be converted to {@link FieldError FieldErrors}, 071 * collected in the Errors instance, using the following error codes: 072 * 073 * <ul> 074 * <li>Missing field error: "required" 075 * <li>Type mismatch error: "typeMismatch" 076 * <li>Method invocation error: "methodInvocation" 077 * </ul> 078 * 079 * <p>By default, binding errors get resolved through the {@link BindingErrorProcessor} 080 * strategy, processing for missing fields and property access exceptions: see the 081 * {@link #setBindingErrorProcessor} method. You can override the default strategy 082 * if needed, for example to generate different error codes. 083 * 084 * <p>Custom validation errors can be added afterwards. You will typically want to resolve 085 * such error codes into proper user-visible error messages; this can be achieved through 086 * resolving each error via a {@link org.springframework.context.MessageSource}, which is 087 * able to resolve an {@link ObjectError}/{@link FieldError} through its 088 * {@link org.springframework.context.MessageSource#getMessage(org.springframework.context.MessageSourceResolvable, java.util.Locale)} 089 * method. The list of message codes can be customized through the {@link MessageCodesResolver} 090 * strategy: see the {@link #setMessageCodesResolver} method. {@link DefaultMessageCodesResolver}'s 091 * javadoc states details on the default resolution rules. 092 * 093 * <p>This generic data binder can be used in any kind of environment. 094 * It is typically used by Spring web MVC controllers, via the web-specific 095 * subclasses {@link org.springframework.web.bind.ServletRequestDataBinder} 096 * and {@link org.springframework.web.portlet.bind.PortletRequestDataBinder}. 097 * 098 * @author Rod Johnson 099 * @author Juergen Hoeller 100 * @author Rob Harrop 101 * @author Stephane Nicoll 102 * @author Kazuki Shimizu 103 * @see #setAllowedFields 104 * @see #setRequiredFields 105 * @see #registerCustomEditor 106 * @see #setMessageCodesResolver 107 * @see #setBindingErrorProcessor 108 * @see #bind 109 * @see #getBindingResult 110 * @see DefaultMessageCodesResolver 111 * @see DefaultBindingErrorProcessor 112 * @see org.springframework.context.MessageSource 113 * @see org.springframework.web.bind.ServletRequestDataBinder 114 */ 115public class DataBinder implements PropertyEditorRegistry, TypeConverter { 116 117 /** Default object name used for binding: "target" */ 118 public static final String DEFAULT_OBJECT_NAME = "target"; 119 120 /** Default limit for array and collection growing: 256 */ 121 public static final int DEFAULT_AUTO_GROW_COLLECTION_LIMIT = 256; 122 123 124 /** 125 * We'll create a lot of DataBinder instances: Let's use a static logger. 126 */ 127 protected static final Log logger = LogFactory.getLog(DataBinder.class); 128 129 private static Class<?> javaUtilOptionalClass = null; 130 131 static { 132 try { 133 javaUtilOptionalClass = 134 ClassUtils.forName("java.util.Optional", DataBinder.class.getClassLoader()); 135 } 136 catch (ClassNotFoundException ex) { 137 // Java 8 not available - Optional references simply not supported then. 138 } 139 } 140 141 142 private final Object target; 143 144 private final String objectName; 145 146 private AbstractPropertyBindingResult bindingResult; 147 148 private SimpleTypeConverter typeConverter; 149 150 private boolean ignoreUnknownFields = true; 151 152 private boolean ignoreInvalidFields = false; 153 154 private boolean autoGrowNestedPaths = true; 155 156 private int autoGrowCollectionLimit = DEFAULT_AUTO_GROW_COLLECTION_LIMIT; 157 158 private String[] allowedFields; 159 160 private String[] disallowedFields; 161 162 private String[] requiredFields; 163 164 private ConversionService conversionService; 165 166 private MessageCodesResolver messageCodesResolver; 167 168 private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor(); 169 170 private final List<Validator> validators = new ArrayList<Validator>(); 171 172 173 /** 174 * Create a new DataBinder instance, with default object name. 175 * @param target the target object to bind onto (or {@code null} 176 * if the binder is just used to convert a plain parameter value) 177 * @see #DEFAULT_OBJECT_NAME 178 */ 179 public DataBinder(Object target) { 180 this(target, DEFAULT_OBJECT_NAME); 181 } 182 183 /** 184 * Create a new DataBinder instance. 185 * @param target the target object to bind onto (or {@code null} 186 * if the binder is just used to convert a plain parameter value) 187 * @param objectName the name of the target object 188 */ 189 public DataBinder(Object target, String objectName) { 190 if (target != null && target.getClass() == javaUtilOptionalClass) { 191 this.target = OptionalUnwrapper.unwrap(target); 192 } 193 else { 194 this.target = target; 195 } 196 this.objectName = objectName; 197 } 198 199 200 /** 201 * Return the wrapped target object. 202 */ 203 public Object getTarget() { 204 return this.target; 205 } 206 207 /** 208 * Return the name of the bound object. 209 */ 210 public String getObjectName() { 211 return this.objectName; 212 } 213 214 /** 215 * Set whether this binder should attempt to "auto-grow" a nested path that contains a null value. 216 * <p>If "true", a null path location will be populated with a default object value and traversed 217 * instead of resulting in an exception. This flag also enables auto-growth of collection elements 218 * when accessing an out-of-bounds index. 219 * <p>Default is "true" on a standard DataBinder. Note that since Spring 4.1 this feature is supported 220 * for bean property access (DataBinder's default mode) and field access. 221 * @see #initBeanPropertyAccess() 222 * @see org.springframework.beans.BeanWrapper#setAutoGrowNestedPaths 223 */ 224 public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) { 225 Assert.state(this.bindingResult == null, 226 "DataBinder is already initialized - call setAutoGrowNestedPaths before other configuration methods"); 227 this.autoGrowNestedPaths = autoGrowNestedPaths; 228 } 229 230 /** 231 * Return whether "auto-growing" of nested paths has been activated. 232 */ 233 public boolean isAutoGrowNestedPaths() { 234 return this.autoGrowNestedPaths; 235 } 236 237 /** 238 * Specify the limit for array and collection auto-growing. 239 * <p>Default is 256, preventing OutOfMemoryErrors in case of large indexes. 240 * Raise this limit if your auto-growing needs are unusually high. 241 * @see #initBeanPropertyAccess() 242 * @see org.springframework.beans.BeanWrapper#setAutoGrowCollectionLimit 243 */ 244 public void setAutoGrowCollectionLimit(int autoGrowCollectionLimit) { 245 Assert.state(this.bindingResult == null, 246 "DataBinder is already initialized - call setAutoGrowCollectionLimit before other configuration methods"); 247 this.autoGrowCollectionLimit = autoGrowCollectionLimit; 248 } 249 250 /** 251 * Return the current limit for array and collection auto-growing. 252 */ 253 public int getAutoGrowCollectionLimit() { 254 return this.autoGrowCollectionLimit; 255 } 256 257 /** 258 * Initialize standard JavaBean property access for this DataBinder. 259 * <p>This is the default; an explicit call just leads to eager initialization. 260 * @see #initDirectFieldAccess() 261 * @see #createBeanPropertyBindingResult() 262 */ 263 public void initBeanPropertyAccess() { 264 Assert.state(this.bindingResult == null, 265 "DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods"); 266 this.bindingResult = createBeanPropertyBindingResult(); 267 } 268 269 /** 270 * Create the {@link AbstractPropertyBindingResult} instance using standard 271 * JavaBean property access. 272 * @since 4.2.1 273 */ 274 protected AbstractPropertyBindingResult createBeanPropertyBindingResult() { 275 BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(), 276 getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit()); 277 278 if (this.conversionService != null) { 279 result.initConversion(this.conversionService); 280 } 281 if (this.messageCodesResolver != null) { 282 result.setMessageCodesResolver(this.messageCodesResolver); 283 } 284 285 return result; 286 } 287 288 /** 289 * Initialize direct field access for this DataBinder, 290 * as alternative to the default bean property access. 291 * @see #initBeanPropertyAccess() 292 * @see #createDirectFieldBindingResult() 293 */ 294 public void initDirectFieldAccess() { 295 Assert.state(this.bindingResult == null, 296 "DataBinder is already initialized - call initDirectFieldAccess before other configuration methods"); 297 this.bindingResult = createDirectFieldBindingResult(); 298 } 299 300 /** 301 * Create the {@link AbstractPropertyBindingResult} instance using direct 302 * field access. 303 * @since 4.2.1 304 */ 305 protected AbstractPropertyBindingResult createDirectFieldBindingResult() { 306 DirectFieldBindingResult result = new DirectFieldBindingResult(getTarget(), 307 getObjectName(), isAutoGrowNestedPaths()); 308 309 if (this.conversionService != null) { 310 result.initConversion(this.conversionService); 311 } 312 if (this.messageCodesResolver != null) { 313 result.setMessageCodesResolver(this.messageCodesResolver); 314 } 315 316 return result; 317 } 318 319 /** 320 * Return the internal BindingResult held by this DataBinder, 321 * as an AbstractPropertyBindingResult. 322 */ 323 protected AbstractPropertyBindingResult getInternalBindingResult() { 324 if (this.bindingResult == null) { 325 initBeanPropertyAccess(); 326 } 327 return this.bindingResult; 328 } 329 330 /** 331 * Return the underlying PropertyAccessor of this binder's BindingResult. 332 */ 333 protected ConfigurablePropertyAccessor getPropertyAccessor() { 334 return getInternalBindingResult().getPropertyAccessor(); 335 } 336 337 /** 338 * Return this binder's underlying SimpleTypeConverter. 339 */ 340 protected SimpleTypeConverter getSimpleTypeConverter() { 341 if (this.typeConverter == null) { 342 this.typeConverter = new SimpleTypeConverter(); 343 if (this.conversionService != null) { 344 this.typeConverter.setConversionService(this.conversionService); 345 } 346 } 347 return this.typeConverter; 348 } 349 350 /** 351 * Return the underlying TypeConverter of this binder's BindingResult. 352 */ 353 protected PropertyEditorRegistry getPropertyEditorRegistry() { 354 if (getTarget() != null) { 355 return getInternalBindingResult().getPropertyAccessor(); 356 } 357 else { 358 return getSimpleTypeConverter(); 359 } 360 } 361 362 /** 363 * Return the underlying TypeConverter of this binder's BindingResult. 364 */ 365 protected TypeConverter getTypeConverter() { 366 if (getTarget() != null) { 367 return getInternalBindingResult().getPropertyAccessor(); 368 } 369 else { 370 return getSimpleTypeConverter(); 371 } 372 } 373 374 /** 375 * Return the BindingResult instance created by this DataBinder. 376 * This allows for convenient access to the binding results after 377 * a bind operation. 378 * @return the BindingResult instance, to be treated as BindingResult 379 * or as Errors instance (Errors is a super-interface of BindingResult) 380 * @see Errors 381 * @see #bind 382 */ 383 public BindingResult getBindingResult() { 384 return getInternalBindingResult(); 385 } 386 387 388 /** 389 * Set whether to ignore unknown fields, that is, whether to ignore bind 390 * parameters that do not have corresponding fields in the target object. 391 * <p>Default is "true". Turn this off to enforce that all bind parameters 392 * must have a matching field in the target object. 393 * <p>Note that this setting only applies to <i>binding</i> operations 394 * on this DataBinder, not to <i>retrieving</i> values via its 395 * {@link #getBindingResult() BindingResult}. 396 * @see #bind 397 */ 398 public void setIgnoreUnknownFields(boolean ignoreUnknownFields) { 399 this.ignoreUnknownFields = ignoreUnknownFields; 400 } 401 402 /** 403 * Return whether to ignore unknown fields when binding. 404 */ 405 public boolean isIgnoreUnknownFields() { 406 return this.ignoreUnknownFields; 407 } 408 409 /** 410 * Set whether to ignore invalid fields, that is, whether to ignore bind 411 * parameters that have corresponding fields in the target object which are 412 * not accessible (for example because of null values in the nested path). 413 * <p>Default is "false". Turn this on to ignore bind parameters for 414 * nested objects in non-existing parts of the target object graph. 415 * <p>Note that this setting only applies to <i>binding</i> operations 416 * on this DataBinder, not to <i>retrieving</i> values via its 417 * {@link #getBindingResult() BindingResult}. 418 * @see #bind 419 */ 420 public void setIgnoreInvalidFields(boolean ignoreInvalidFields) { 421 this.ignoreInvalidFields = ignoreInvalidFields; 422 } 423 424 /** 425 * Return whether to ignore invalid fields when binding. 426 */ 427 public boolean isIgnoreInvalidFields() { 428 return this.ignoreInvalidFields; 429 } 430 431 /** 432 * Register fields that should be allowed for binding. Default is all 433 * fields. Restrict this for example to avoid unwanted modifications 434 * by malicious users when binding HTTP request parameters. 435 * <p>Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching 436 * can be implemented by overriding the {@code isAllowed} method. 437 * <p>Alternatively, specify a list of <i>disallowed</i> fields. 438 * @param allowedFields array of field names 439 * @see #setDisallowedFields 440 * @see #isAllowed(String) 441 * @see org.springframework.web.bind.ServletRequestDataBinder 442 */ 443 public void setAllowedFields(String... allowedFields) { 444 this.allowedFields = PropertyAccessorUtils.canonicalPropertyNames(allowedFields); 445 } 446 447 /** 448 * Return the fields that should be allowed for binding. 449 * @return array of field names 450 */ 451 public String[] getAllowedFields() { 452 return this.allowedFields; 453 } 454 455 /** 456 * Register fields that should <i>not</i> be allowed for binding. Default is none. 457 * Mark fields as disallowed for example to avoid unwanted modifications 458 * by malicious users when binding HTTP request parameters. 459 * <p>Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching 460 * can be implemented by overriding the {@code isAllowed} method. 461 * <p>Alternatively, specify a list of <i>allowed</i> fields. 462 * @param disallowedFields array of field names 463 * @see #setAllowedFields 464 * @see #isAllowed(String) 465 * @see org.springframework.web.bind.ServletRequestDataBinder 466 */ 467 public void setDisallowedFields(String... disallowedFields) { 468 this.disallowedFields = PropertyAccessorUtils.canonicalPropertyNames(disallowedFields); 469 } 470 471 /** 472 * Return the fields that should <i>not</i> be allowed for binding. 473 * @return array of field names 474 */ 475 public String[] getDisallowedFields() { 476 return this.disallowedFields; 477 } 478 479 /** 480 * Register fields that are required for each binding process. 481 * <p>If one of the specified fields is not contained in the list of 482 * incoming property values, a corresponding "missing field" error 483 * will be created, with error code "required" (by the default 484 * binding error processor). 485 * @param requiredFields array of field names 486 * @see #setBindingErrorProcessor 487 * @see DefaultBindingErrorProcessor#MISSING_FIELD_ERROR_CODE 488 */ 489 public void setRequiredFields(String... requiredFields) { 490 this.requiredFields = PropertyAccessorUtils.canonicalPropertyNames(requiredFields); 491 if (logger.isDebugEnabled()) { 492 logger.debug("DataBinder requires binding of required fields [" + 493 StringUtils.arrayToCommaDelimitedString(requiredFields) + "]"); 494 } 495 } 496 497 /** 498 * Return the fields that are required for each binding process. 499 * @return array of field names 500 */ 501 public String[] getRequiredFields() { 502 return this.requiredFields; 503 } 504 505 /** 506 * Set whether to extract the old field value when applying a 507 * property editor to a new value for a field. 508 * <p>Default is "true", exposing previous field values to custom editors. 509 * Turn this to "false" to avoid side effects caused by getters. 510 * @deprecated as of Spring 4.3.5, in favor of customizing this in 511 * {@link #createBeanPropertyBindingResult()} or 512 * {@link #createDirectFieldBindingResult()} itself 513 */ 514 @Deprecated 515 public void setExtractOldValueForEditor(boolean extractOldValueForEditor) { 516 getPropertyAccessor().setExtractOldValueForEditor(extractOldValueForEditor); 517 } 518 519 /** 520 * Set the strategy to use for resolving errors into message codes. 521 * Applies the given strategy to the underlying errors holder. 522 * <p>Default is a DefaultMessageCodesResolver. 523 * @see BeanPropertyBindingResult#setMessageCodesResolver 524 * @see DefaultMessageCodesResolver 525 */ 526 public void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) { 527 Assert.state(this.messageCodesResolver == null, "DataBinder is already initialized with MessageCodesResolver"); 528 this.messageCodesResolver = messageCodesResolver; 529 if (this.bindingResult != null && messageCodesResolver != null) { 530 this.bindingResult.setMessageCodesResolver(messageCodesResolver); 531 } 532 } 533 534 /** 535 * Set the strategy to use for processing binding errors, that is, 536 * required field errors and {@code PropertyAccessException}s. 537 * <p>Default is a DefaultBindingErrorProcessor. 538 * @see DefaultBindingErrorProcessor 539 */ 540 public void setBindingErrorProcessor(BindingErrorProcessor bindingErrorProcessor) { 541 Assert.notNull(bindingErrorProcessor, "BindingErrorProcessor must not be null"); 542 this.bindingErrorProcessor = bindingErrorProcessor; 543 } 544 545 /** 546 * Return the strategy for processing binding errors. 547 */ 548 public BindingErrorProcessor getBindingErrorProcessor() { 549 return this.bindingErrorProcessor; 550 } 551 552 /** 553 * Set the Validator to apply after each binding step. 554 * @see #addValidators(Validator...) 555 * @see #replaceValidators(Validator...) 556 */ 557 public void setValidator(Validator validator) { 558 assertValidators(validator); 559 this.validators.clear(); 560 this.validators.add(validator); 561 } 562 563 private void assertValidators(Validator... validators) { 564 Assert.notNull(validators, "Validators required"); 565 Object target = getTarget(); 566 for (Validator validator : validators) { 567 if (validator != null && (target != null && !validator.supports(target.getClass()))) { 568 throw new IllegalStateException("Invalid target for Validator [" + validator + "]: " + target); 569 } 570 } 571 } 572 573 /** 574 * Add Validators to apply after each binding step. 575 * @see #setValidator(Validator) 576 * @see #replaceValidators(Validator...) 577 */ 578 public void addValidators(Validator... validators) { 579 assertValidators(validators); 580 this.validators.addAll(Arrays.asList(validators)); 581 } 582 583 /** 584 * Replace the Validators to apply after each binding step. 585 * @see #setValidator(Validator) 586 * @see #addValidators(Validator...) 587 */ 588 public void replaceValidators(Validator... validators) { 589 assertValidators(validators); 590 this.validators.clear(); 591 this.validators.addAll(Arrays.asList(validators)); 592 } 593 594 /** 595 * Return the primary Validator to apply after each binding step, if any. 596 */ 597 public Validator getValidator() { 598 return (this.validators.size() > 0 ? this.validators.get(0) : null); 599 } 600 601 /** 602 * Return the Validators to apply after data binding. 603 */ 604 public List<Validator> getValidators() { 605 return Collections.unmodifiableList(this.validators); 606 } 607 608 609 //--------------------------------------------------------------------- 610 // Implementation of PropertyEditorRegistry/TypeConverter interface 611 //--------------------------------------------------------------------- 612 613 /** 614 * Specify a Spring 3.0 ConversionService to use for converting 615 * property values, as an alternative to JavaBeans PropertyEditors. 616 */ 617 public void setConversionService(ConversionService conversionService) { 618 Assert.state(this.conversionService == null, "DataBinder is already initialized with ConversionService"); 619 this.conversionService = conversionService; 620 if (this.bindingResult != null && conversionService != null) { 621 this.bindingResult.initConversion(conversionService); 622 } 623 } 624 625 /** 626 * Return the associated ConversionService, if any. 627 */ 628 public ConversionService getConversionService() { 629 return this.conversionService; 630 } 631 632 /** 633 * Add a custom formatter, applying it to all fields matching the 634 * {@link Formatter}-declared type. 635 * <p>Registers a corresponding {@link PropertyEditor} adapter underneath the covers. 636 * @param formatter the formatter to add, generically declared for a specific type 637 * @since 4.2 638 * @see #registerCustomEditor(Class, PropertyEditor) 639 */ 640 public void addCustomFormatter(Formatter<?> formatter) { 641 FormatterPropertyEditorAdapter adapter = new FormatterPropertyEditorAdapter(formatter); 642 getPropertyEditorRegistry().registerCustomEditor(adapter.getFieldType(), adapter); 643 } 644 645 /** 646 * Add a custom formatter for the field type specified in {@link Formatter} class, 647 * applying it to the specified fields only, if any, or otherwise to all fields. 648 * <p>Registers a corresponding {@link PropertyEditor} adapter underneath the covers. 649 * @param formatter the formatter to add, generically declared for a specific type 650 * @param fields the fields to apply the formatter to, or none if to be applied to all 651 * @since 4.2 652 * @see #registerCustomEditor(Class, String, PropertyEditor) 653 */ 654 public void addCustomFormatter(Formatter<?> formatter, String... fields) { 655 FormatterPropertyEditorAdapter adapter = new FormatterPropertyEditorAdapter(formatter); 656 Class<?> fieldType = adapter.getFieldType(); 657 if (ObjectUtils.isEmpty(fields)) { 658 getPropertyEditorRegistry().registerCustomEditor(fieldType, adapter); 659 } 660 else { 661 for (String field : fields) { 662 getPropertyEditorRegistry().registerCustomEditor(fieldType, field, adapter); 663 } 664 } 665 } 666 667 /** 668 * Add a custom formatter, applying it to the specified field types only, if any, 669 * or otherwise to all fields matching the {@link Formatter}-declared type. 670 * <p>Registers a corresponding {@link PropertyEditor} adapter underneath the covers. 671 * @param formatter the formatter to add (does not need to generically declare a 672 * field type if field types are explicitly specified as parameters) 673 * @param fieldTypes the field types to apply the formatter to, or none if to be 674 * derived from the given {@link Formatter} implementation class 675 * @since 4.2 676 * @see #registerCustomEditor(Class, PropertyEditor) 677 */ 678 public void addCustomFormatter(Formatter<?> formatter, Class<?>... fieldTypes) { 679 FormatterPropertyEditorAdapter adapter = new FormatterPropertyEditorAdapter(formatter); 680 if (ObjectUtils.isEmpty(fieldTypes)) { 681 getPropertyEditorRegistry().registerCustomEditor(adapter.getFieldType(), adapter); 682 } 683 else { 684 for (Class<?> fieldType : fieldTypes) { 685 getPropertyEditorRegistry().registerCustomEditor(fieldType, adapter); 686 } 687 } 688 } 689 690 @Override 691 public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) { 692 getPropertyEditorRegistry().registerCustomEditor(requiredType, propertyEditor); 693 } 694 695 @Override 696 public void registerCustomEditor(Class<?> requiredType, String field, PropertyEditor propertyEditor) { 697 getPropertyEditorRegistry().registerCustomEditor(requiredType, field, propertyEditor); 698 } 699 700 @Override 701 public PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath) { 702 return getPropertyEditorRegistry().findCustomEditor(requiredType, propertyPath); 703 } 704 705 @Override 706 public <T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException { 707 return getTypeConverter().convertIfNecessary(value, requiredType); 708 } 709 710 @Override 711 public <T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam) 712 throws TypeMismatchException { 713 714 return getTypeConverter().convertIfNecessary(value, requiredType, methodParam); 715 } 716 717 @Override 718 public <T> T convertIfNecessary(Object value, Class<T> requiredType, Field field) 719 throws TypeMismatchException { 720 721 return getTypeConverter().convertIfNecessary(value, requiredType, field); 722 } 723 724 725 /** 726 * Bind the given property values to this binder's target. 727 * <p>This call can create field errors, representing basic binding 728 * errors like a required field (code "required"), or type mismatch 729 * between value and bean property (code "typeMismatch"). 730 * <p>Note that the given PropertyValues should be a throwaway instance: 731 * For efficiency, it will be modified to just contain allowed fields if it 732 * implements the MutablePropertyValues interface; else, an internal mutable 733 * copy will be created for this purpose. Pass in a copy of the PropertyValues 734 * if you want your original instance to stay unmodified in any case. 735 * @param pvs property values to bind 736 * @see #doBind(org.springframework.beans.MutablePropertyValues) 737 */ 738 public void bind(PropertyValues pvs) { 739 MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues) ? 740 (MutablePropertyValues) pvs : new MutablePropertyValues(pvs); 741 doBind(mpvs); 742 } 743 744 /** 745 * Actual implementation of the binding process, working with the 746 * passed-in MutablePropertyValues instance. 747 * @param mpvs the property values to bind, 748 * as MutablePropertyValues instance 749 * @see #checkAllowedFields 750 * @see #checkRequiredFields 751 * @see #applyPropertyValues 752 */ 753 protected void doBind(MutablePropertyValues mpvs) { 754 checkAllowedFields(mpvs); 755 checkRequiredFields(mpvs); 756 applyPropertyValues(mpvs); 757 } 758 759 /** 760 * Check the given property values against the allowed fields, 761 * removing values for fields that are not allowed. 762 * @param mpvs the property values to be bound (can be modified) 763 * @see #getAllowedFields 764 * @see #isAllowed(String) 765 */ 766 protected void checkAllowedFields(MutablePropertyValues mpvs) { 767 PropertyValue[] pvs = mpvs.getPropertyValues(); 768 for (PropertyValue pv : pvs) { 769 String field = PropertyAccessorUtils.canonicalPropertyName(pv.getName()); 770 if (!isAllowed(field)) { 771 mpvs.removePropertyValue(pv); 772 getBindingResult().recordSuppressedField(field); 773 if (logger.isDebugEnabled()) { 774 logger.debug("Field [" + field + "] has been removed from PropertyValues " + 775 "and will not be bound, because it has not been found in the list of allowed fields"); 776 } 777 } 778 } 779 } 780 781 /** 782 * Return if the given field is allowed for binding. 783 * Invoked for each passed-in property value. 784 * <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, 785 * as well as direct equality, in the specified lists of allowed fields and 786 * disallowed fields. A field matching a disallowed pattern will not be accepted 787 * even if it also happens to match a pattern in the allowed list. 788 * <p>Can be overridden in subclasses. 789 * @param field the field to check 790 * @return if the field is allowed 791 * @see #setAllowedFields 792 * @see #setDisallowedFields 793 * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String) 794 */ 795 protected boolean isAllowed(String field) { 796 String[] allowed = getAllowedFields(); 797 String[] disallowed = getDisallowedFields(); 798 return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) && 799 (ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field))); 800 } 801 802 /** 803 * Check the given property values against the required fields, 804 * generating missing field errors where appropriate. 805 * @param mpvs the property values to be bound (can be modified) 806 * @see #getRequiredFields 807 * @see #getBindingErrorProcessor 808 * @see BindingErrorProcessor#processMissingFieldError 809 */ 810 protected void checkRequiredFields(MutablePropertyValues mpvs) { 811 String[] requiredFields = getRequiredFields(); 812 if (!ObjectUtils.isEmpty(requiredFields)) { 813 Map<String, PropertyValue> propertyValues = new HashMap<String, PropertyValue>(); 814 PropertyValue[] pvs = mpvs.getPropertyValues(); 815 for (PropertyValue pv : pvs) { 816 String canonicalName = PropertyAccessorUtils.canonicalPropertyName(pv.getName()); 817 propertyValues.put(canonicalName, pv); 818 } 819 for (String field : requiredFields) { 820 PropertyValue pv = propertyValues.get(field); 821 boolean empty = (pv == null || pv.getValue() == null); 822 if (!empty) { 823 if (pv.getValue() instanceof String) { 824 empty = !StringUtils.hasText((String) pv.getValue()); 825 } 826 else if (pv.getValue() instanceof String[]) { 827 String[] values = (String[]) pv.getValue(); 828 empty = (values.length == 0 || !StringUtils.hasText(values[0])); 829 } 830 } 831 if (empty) { 832 // Use bind error processor to create FieldError. 833 getBindingErrorProcessor().processMissingFieldError(field, getInternalBindingResult()); 834 // Remove property from property values to bind: 835 // It has already caused a field error with a rejected value. 836 if (pv != null) { 837 mpvs.removePropertyValue(pv); 838 propertyValues.remove(field); 839 } 840 } 841 } 842 } 843 } 844 845 /** 846 * Apply given property values to the target object. 847 * <p>Default implementation applies all of the supplied property 848 * values as bean property values. By default, unknown fields will 849 * be ignored. 850 * @param mpvs the property values to be bound (can be modified) 851 * @see #getTarget 852 * @see #getPropertyAccessor 853 * @see #isIgnoreUnknownFields 854 * @see #getBindingErrorProcessor 855 * @see BindingErrorProcessor#processPropertyAccessException 856 */ 857 protected void applyPropertyValues(MutablePropertyValues mpvs) { 858 try { 859 // Bind request parameters onto target object. 860 getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields()); 861 } 862 catch (PropertyBatchUpdateException ex) { 863 // Use bind error processor to create FieldErrors. 864 for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) { 865 getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult()); 866 } 867 } 868 } 869 870 871 /** 872 * Invoke the specified Validators, if any. 873 * @see #setValidator(Validator) 874 * @see #getBindingResult() 875 */ 876 public void validate() { 877 for (Validator validator : this.validators) { 878 validator.validate(getTarget(), getBindingResult()); 879 } 880 } 881 882 /** 883 * Invoke the specified Validators, if any, with the given validation hints. 884 * <p>Note: Validation hints may get ignored by the actual target Validator. 885 * @param validationHints one or more hint objects to be passed to a {@link SmartValidator} 886 * @see #setValidator(Validator) 887 * @see SmartValidator#validate(Object, Errors, Object...) 888 */ 889 public void validate(Object... validationHints) { 890 for (Validator validator : getValidators()) { 891 if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) { 892 ((SmartValidator) validator).validate(getTarget(), getBindingResult(), validationHints); 893 } 894 else if (validator != null) { 895 validator.validate(getTarget(), getBindingResult()); 896 } 897 } 898 } 899 900 /** 901 * Close this DataBinder, which may result in throwing 902 * a BindException if it encountered any errors. 903 * @return the model Map, containing target object and Errors instance 904 * @throws BindException if there were any errors in the bind operation 905 * @see BindingResult#getModel() 906 */ 907 public Map<?, ?> close() throws BindException { 908 if (getBindingResult().hasErrors()) { 909 throw new BindException(getBindingResult()); 910 } 911 return getBindingResult().getModel(); 912 } 913 914 915 /** 916 * Inner class to avoid a hard dependency on Java 8. 917 */ 918 @UsesJava8 919 private static class OptionalUnwrapper { 920 921 public static Object unwrap(Object optionalObject) { 922 Optional<?> optional = (Optional<?>) optionalObject; 923 if (!optional.isPresent()) { 924 return null; 925 } 926 Object result = optional.get(); 927 Assert.isTrue(!(result instanceof Optional), "Multi-level Optional usage not supported"); 928 return result; 929 } 930 } 931 932}