From 3af9d1f29bc862870b7eecd50c9eeb587ebbc320 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 31 Jan 2014 22:39:20 +0100 Subject: [PATCH] Consistent iteration over actualValue in Velocity and FreeMarker macros This requires consistent exposure of an actualValue in BindStatus, even if no BindingResult available. Issue: SPR-10837 (cherry picked from commit 4f60b98) --- .../web/servlet/support/BindStatus.java | 22 +++++++----- .../web/servlet/view/velocity/spring.vm | 36 ++++++++----------- .../view/freemarker/FreeMarkerMacroTests.java | 25 +++++++------ .../web/servlet/view/freemarker/test.ftl | 2 +- .../view/velocity/VelocityMacroTests.java | 18 +++++----- .../web/servlet/view/velocity/test.vm | 2 +- 6 files changed, 51 insertions(+), 54 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/BindStatus.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/BindStatus.java index c5f9e6a3e32..4fd4058ef15 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/BindStatus.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/BindStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,13 +61,13 @@ public class BindStatus { private Object value; - private Class valueType; + private Class valueType; private Object actualValue; private PropertyEditor editor; - private List objectErrors; + private List objectErrors; private String[] errorCodes; @@ -90,7 +90,7 @@ public class BindStatus { this.htmlEscape = htmlEscape; // determine name of the object and property - String beanName = null; + String beanName; int dotPos = path.indexOf('.'); if (dotPos == -1) { // property not set, only the object itself @@ -124,6 +124,9 @@ public class BindStatus { this.actualValue = this.bindingResult.getRawFieldValue(this.expression); this.editor = this.bindingResult.findEditor(this.expression, null); } + else { + this.actualValue = this.value; + } } } else { @@ -143,8 +146,9 @@ public class BindStatus { } if (this.expression != null && !"*".equals(this.expression) && !this.expression.endsWith("*")) { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(target); - this.valueType = bw.getPropertyType(this.expression); this.value = bw.getPropertyValue(this.expression); + this.valueType = bw.getPropertyType(this.expression); + this.actualValue = this.value; } this.errorCodes = new String[0]; this.errorMessages = new String[0]; @@ -161,7 +165,7 @@ public class BindStatus { private void initErrorCodes() { this.errorCodes = new String[this.objectErrors.size()]; for (int i = 0; i < this.objectErrors.size(); i++) { - ObjectError error = (ObjectError) this.objectErrors.get(i); + ObjectError error = this.objectErrors.get(i); this.errorCodes[i] = error.getCode(); } } @@ -173,7 +177,7 @@ public class BindStatus { if (this.errorMessages == null) { this.errorMessages = new String[this.objectErrors.size()]; for (int i = 0; i < this.objectErrors.size(); i++) { - ObjectError error = (ObjectError) this.objectErrors.get(i); + ObjectError error = this.objectErrors.get(i); this.errorMessages[i] = this.requestContext.getMessage(error, this.htmlEscape); } } @@ -214,7 +218,7 @@ public class BindStatus { * '{@code getValue().getClass()}' since '{@code getValue()}' may * return '{@code null}'. */ - public Class getValueType() { + public Class getValueType() { return this.valueType; } @@ -318,7 +322,7 @@ public class BindStatus { * @param valueClass the value class that an editor is needed for * @return the associated PropertyEditor, or {@code null} if none */ - public PropertyEditor findEditor(Class valueClass) { + public PropertyEditor findEditor(Class valueClass) { return (this.bindingResult != null ? this.bindingResult.findEditor(this.expression, valueClass) : null); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/velocity/spring.vm b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/velocity/spring.vm index 7126ab3cd93..9c1af588413 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/velocity/spring.vm +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/velocity/spring.vm @@ -107,7 +107,7 @@ * * @param path the name of the field to bind to * @param attributes any additional attributes for the element (such as class - * or CSS styles or size + * or CSS styles or size) * *# #macro( springFormInput $path $attributes ) @@ -124,7 +124,7 @@ * * @param path the name of the field to bind to * @param attributes any additional attributes for the element (such as class - * or CSS styles or size + * or CSS styles or size) * *# #macro( springFormPasswordInput $path $attributes ) @@ -140,7 +140,7 @@ * * @param path the name of the field to bind to * @param attributes any additional attributes for the element (such as class - * or CSS styles or size + * or CSS styles or size) * *# #macro( springFormHiddenInput $path $attributes ) @@ -156,7 +156,7 @@ * * @param path the name of the field to bind to * @param attributes any additional attributes for the element (such as class - * or CSS styles or size + * or CSS styles or size) * *# #macro( springFormTextarea $path $attributes ) @@ -178,16 +178,14 @@ * @param path the name of the field to bind to * @param options a map (value=label) of all the available options * @param attributes any additional attributes for the element (such as class - * or CSS styles or size + * or CSS styles or size) *# #macro( springFormSingleSelect $path $options $attributes ) #springBind($path) @@ -202,17 +200,15 @@ * @param path the name of the field to bind to * @param options a map (value=label) of all the available options * @param attributes any additional attributes for the element (such as class - * or CSS styles or size + * or CSS styles or size) *# #macro( springFormMultiSelect $path $options $attributes ) #springBind($path) ' + * separate each option. Typically ' ' or '
'. * @param attributes any additional attributes for the element (such as class - * or CSS styles or size + * or CSS styles or size) *# #macro( springFormCheckboxes $path $options $separator $attributes ) #springBind($path) #foreach($option in $options.keySet()) ", getMacroOutput("FORM9")); @@ -272,8 +270,8 @@ public class FreeMarkerMacroTests { assertTrue("Wrong output: " + output, output.contains("")); } - private String getMacroOutput(String name) throws Exception { + private String getMacroOutput(String name) throws Exception { String macro = fetchMacro(name); assertNotNull(macro); @@ -296,6 +294,7 @@ public class FreeMarkerMacroTests { fred.setJedi(true); darren.setSpouse(fred); darren.setJedi(true); + darren.setStringArray(new String[] {"John", "Fred"}); request.setAttribute("command", darren); Map names = new HashMap(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/test.ftl b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/test.ftl index b1e01561861..b6fb4caf4ea 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/test.ftl +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/test.ftl @@ -61,7 +61,7 @@ FORM7 <@spring.formRadioButtons "command.name", nameOptionMap, " ", ""/> FORM8 -<@spring.formCheckboxes "command.spouses", nameOptionMap, " ", ""/> +<@spring.formCheckboxes "command.stringArray", nameOptionMap, " ", ""/> FORM9 <@spring.formPasswordInput "command.name", ""/> diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/VelocityMacroTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/VelocityMacroTests.java index 04c8497df3c..d79048789ff 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/VelocityMacroTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/VelocityMacroTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ package org.springframework.web.servlet.view.velocity; import java.util.HashMap; import java.util.Map; - import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; @@ -27,10 +26,10 @@ import org.apache.velocity.Template; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.context.Context; -import org.springframework.tests.sample.beans.TestBean; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.mock.web.test.MockServletContext; +import org.springframework.tests.sample.beans.TestBean; import org.springframework.util.StringUtils; import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; @@ -94,7 +93,7 @@ public class VelocityMacroTests extends TestCase { vv.setApplicationContext(wac); vv.setExposeSpringMacroHelpers(true); - Map model = new HashMap(); + Map model = new HashMap(); model.put("tb", new TestBean("juergen", 99)); vv.render(model, request, response); } @@ -112,7 +111,7 @@ public class VelocityMacroTests extends TestCase { vv.setApplicationContext(wac); vv.setExposeSpringMacroHelpers(true); - Map model = new HashMap(); + Map model = new HashMap(); model.put(VelocityView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE, helperTool); try { @@ -126,11 +125,11 @@ public class VelocityMacroTests extends TestCase { public void testAllMacros() throws Exception { DummyMacroRequestContext rc = new DummyMacroRequestContext(request); - Map msgMap = new HashMap(); + Map msgMap = new HashMap(); msgMap.put("hello", "Howdy"); msgMap.put("world", "Mundo"); rc.setMessageMap(msgMap); - Map themeMsgMap = new HashMap(); + Map themeMsgMap = new HashMap(); themeMsgMap.put("hello", "Howdy!"); themeMsgMap.put("world", "Mundo!"); rc.setThemeMessageMap(themeMsgMap); @@ -138,9 +137,10 @@ public class VelocityMacroTests extends TestCase { TestBean tb = new TestBean("Darren", 99); tb.setJedi(true); + tb.setStringArray(new String[] {"John", "Fred"}); request.setAttribute("command", tb); - HashMap names = new HashMap(); + Map names = new HashMap(); names.put("Darren", "Darren Davison"); names.put("John", "John Doe"); names.put("Fred", "Fred Bloggs"); @@ -149,7 +149,7 @@ public class VelocityMacroTests extends TestCase { vc.setPreferFileSystemAccess(false); VelocityEngine ve = vc.createVelocityEngine(); - Map model = new HashMap(); + Map model = new HashMap(); model.put("command", tb); model.put("springMacroRequestContext", rc); model.put("nameOptionMap", names); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/test.vm b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/test.vm index 401b74134a9..e125e5efed1 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/test.vm +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/test.vm @@ -45,7 +45,7 @@ FORM7 #springFormRadioButtons("command.name" $nameOptionMap " " "") FORM8 -#springFormCheckboxes("command.spouses" $nameOptionMap " " "") +#springFormCheckboxes("command.stringArray" $nameOptionMap " " "") FORM9 #springFormPasswordInput("command.name" "")