|
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,12 +514,46 @@ 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()); |
511 | | - Expression target = expressionParser.parseExpression(parameter, parserContext); |
512 | | - DefaultMapping mapping = new DefaultMapping(source, target); |
513 | | - if (logger.isDebugEnabled()) { |
514 | | - logger.debug("Adding default mapping for parameter '" + parameter + "'"); |
| 517 | + if (expressionParser instanceof BeanWrapperExpressionParser || checkModelProperty(parameter, model)) { |
| 518 | + Expression target = expressionParser.parseExpression(parameter, parserContext); |
| 519 | + DefaultMapping mapping = new DefaultMapping(source, target); |
| 520 | + if (logger.isDebugEnabled()) { |
| 521 | + logger.debug("Adding default mapping for parameter '" + parameter + "'"); |
| 522 | + } |
| 523 | + mapper.addMapping(mapping); |
515 | 524 | } |
516 | | - mapper.addMapping(mapping); |
| 525 | + } |
| 526 | + |
| 527 | + /** |
| 528 | + * Perform basic checks on the given expression to see if it looks like a property path. |
| 529 | + * Check if the top nested property is a readable property on the Model. |
| 530 | + * Check if the remaining nested properties are valid Java identifiers. |
| 531 | + */ |
| 532 | + private boolean checkModelProperty(String expression, Object model) { |
| 533 | + List<String> propertyNames = new ArrayList<String>(); |
| 534 | + while (true) { |
| 535 | + int index = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(expression); |
| 536 | + String nestedProperty = index != -1 ? expression.substring(0, index) : expression; |
| 537 | + nestedProperty = PropertyAccessorUtils.getPropertyName(nestedProperty); |
| 538 | + propertyNames.add(nestedProperty); |
| 539 | + if (index == -1) { |
| 540 | + break; |
| 541 | + } |
| 542 | + if (expression.length() == index + 1) { |
| 543 | + return false; |
| 544 | + } |
| 545 | + expression = expression.substring(index + 1); |
| 546 | + } |
| 547 | + BeanWrapperImpl beanWrapper = new BeanWrapperImpl(model); |
| 548 | + if (!beanWrapper.isReadableProperty(propertyNames.get(0))) { |
| 549 | + return false; |
| 550 | + } |
| 551 | + for (int i=0; i < propertyNames.size(); i++) { |
| 552 | + if (!SourceVersion.isName(propertyNames.get(i))) { |
| 553 | + return false; |
| 554 | + } |
| 555 | + } |
| 556 | + return true; |
517 | 557 | } |
518 | 558 |
|
519 | 559 | // package private |
|
0 commit comments