18
18
import java .io .IOException ;
19
19
import java .io .Serializable ;
20
20
import java .lang .reflect .Array ;
21
+ import java .util .ArrayList ;
21
22
import java .util .HashMap ;
22
23
import java .util .List ;
23
24
import java .util .Map ;
24
25
import java .util .Set ;
25
26
27
+ import javax .lang .model .SourceVersion ;
28
+
26
29
import org .apache .commons .logging .Log ;
27
30
import org .apache .commons .logging .LogFactory ;
31
+
32
+ import org .springframework .beans .BeanWrapperImpl ;
33
+ import org .springframework .beans .PropertyAccessorUtils ;
28
34
import org .springframework .binding .convert .ConversionExecutor ;
29
35
import org .springframework .binding .convert .ConversionService ;
30
36
import org .springframework .binding .expression .EvaluationException ;
@@ -508,6 +514,7 @@ protected void addEmptyValueMapping(DefaultMapper mapper, String field, Object m
508
514
protected void addDefaultMapping (DefaultMapper mapper , String parameter , Object model ) {
509
515
Expression source = new RequestParameterExpression (parameter );
510
516
ParserContext parserContext = new FluentParserContext ().evaluate (model .getClass ());
517
+ validateDataBindingExpression (parameter , model );
511
518
Expression target = expressionParser .parseExpression (parameter , parserContext );
512
519
DefaultMapping mapping = new DefaultMapping (source , target );
513
520
if (logger .isDebugEnabled ()) {
@@ -516,6 +523,49 @@ protected void addDefaultMapping(DefaultMapper mapper, String parameter, Object
516
523
mapper .addMapping (mapping );
517
524
}
518
525
526
+ /**
527
+ * Check that the expression is a property path where each nested property
528
+ * is a {@link SourceVersion#isName(CharSequence) valid Java identifier} and
529
+ * that the first nested property name at least is a valid readable property
530
+ * on the target object.
531
+ */
532
+ private void validateDataBindingExpression (String expression , Object model ) {
533
+
534
+ if (expressionParser instanceof BeanWrapperExpressionParser ) {
535
+ return ;
536
+ }
537
+
538
+ String errorMessage = "Invalid data binding expression: '" + expression + "' " +
539
+ "for target model class '" + model .getClass () + "'" ;
540
+
541
+ List <String > propertyNames = new ArrayList <String >();
542
+ while (true ) {
543
+ int index = PropertyAccessorUtils .getFirstNestedPropertySeparatorIndex (expression );
544
+ String nestedProperty = index != -1 ? expression .substring (0 , index ) : expression ;
545
+ nestedProperty = PropertyAccessorUtils .getPropertyName (nestedProperty );
546
+ propertyNames .add (nestedProperty );
547
+ if (index == -1 ) {
548
+ break ;
549
+ }
550
+ if (expression .length () == index + 1 ) {
551
+ throw new IllegalStateException (errorMessage );
552
+ }
553
+ expression = expression .substring (index + 1 );
554
+ }
555
+
556
+ BeanWrapperImpl beanWrapper = new BeanWrapperImpl (model );
557
+ if (!beanWrapper .isReadableProperty (propertyNames .get (0 ))) {
558
+ throw new IllegalStateException (errorMessage );
559
+ }
560
+
561
+ for (int i =0 ; i < propertyNames .size (); i ++) {
562
+ if (!SourceVersion .isName (propertyNames .get (i ))) {
563
+ throw new IllegalStateException (errorMessage );
564
+ }
565
+ }
566
+
567
+ }
568
+
519
569
// package private
520
570
521
571
/**
0 commit comments