From 6390e897b8f44780773022424b1d618b7afa6467 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Fri, 12 Feb 2010 17:42:41 +0000 Subject: [PATCH] eval tag tidying --- .../AnnotationDrivenBeanDefinitionParser.java | 28 ++++-- .../ConversionServiceHandlerInterceptor.java | 55 +++++++++++ .../web/servlet/tags/EvalTag.java | 24 +++-- .../mock/web/MockPageContext.java | 10 +- .../web/servlet/config/MvcNamespaceTests.java | 93 ++++++++++++------- .../web/servlet/tags/EvalTagTests.java | 45 ++++++++- 6 files changed, 205 insertions(+), 50 deletions(-) create mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/ConversionServiceHandlerInterceptor.java diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java index 55db545c888..9b81b2a7606 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java @@ -38,6 +38,8 @@ import org.springframework.util.ClassUtils; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; +import org.springframework.web.servlet.handler.ConversionServiceHandlerInterceptor; +import org.springframework.web.servlet.handler.MappedInterceptor; import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter; import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping; @@ -94,28 +96,42 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { annMappingDef.getPropertyValues().add("order", 0); String annMappingName = parserContext.getReaderContext().registerWithGeneratedName(annMappingDef); + RuntimeBeanReference conversionService = getConversionService(element, source, parserContext); + RuntimeBeanReference validator = getValidator(element, source, parserContext); + RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class); bindingDef.setSource(source); bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - bindingDef.getPropertyValues().add("conversionService", getConversionService(element, source, parserContext)); - bindingDef.getPropertyValues().add("validator", getValidator(element, source, parserContext)); + bindingDef.getPropertyValues().add("conversionService", conversionService); + bindingDef.getPropertyValues().add("validator", validator); RootBeanDefinition annAdapterDef = new RootBeanDefinition(AnnotationMethodHandlerAdapter.class); annAdapterDef.setSource(source); annAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); annAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef); annAdapterDef.getPropertyValues().add("messageConverters", getMessageConverters(source)); - String adapterName = parserContext.getReaderContext().registerWithGeneratedName(annAdapterDef); + String annAdapterName = parserContext.getReaderContext().registerWithGeneratedName(annAdapterDef); + + RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceHandlerInterceptor.class); + csInterceptorDef.setSource(source); + csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService); + RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class); + mappedCsInterceptorDef.setSource(source); + mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null); + mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef); + String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef); parserContext.registerComponent(new BeanComponentDefinition(annMappingDef, annMappingName)); - parserContext.registerComponent(new BeanComponentDefinition(annAdapterDef, adapterName)); + parserContext.registerComponent(new BeanComponentDefinition(annAdapterDef, annAdapterName)); + parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName)); parserContext.popAndRegisterContainingComponent(); return null; } - private Object getConversionService(Element element, Object source, ParserContext parserContext) { + private RuntimeBeanReference getConversionService(Element element, Object source, ParserContext parserContext) { if (element.hasAttribute("conversion-service")) { return new RuntimeBeanReference(element.getAttribute("conversion-service")); } @@ -129,7 +145,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { } } - private Object getValidator(Element element, Object source, ParserContext parserContext) { + private RuntimeBeanReference getValidator(Element element, Object source, ParserContext parserContext) { if (element.hasAttribute("validator")) { return new RuntimeBeanReference(element.getAttribute("validator")); } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/ConversionServiceHandlerInterceptor.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/ConversionServiceHandlerInterceptor.java new file mode 100644 index 00000000000..2a72e4880ca --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/ConversionServiceHandlerInterceptor.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002-2008 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.handler; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.core.convert.ConversionService; +import org.springframework.util.Assert; + +/** + * Interceptor that places the configured {@link ConversionService} in request scope so it's available during request processing. + * Mainly for use within JSP tags such as the spring:eval tag. + * The request attribute name is "org.springframework.core.convert.ConversionService", the value of ConversionService.class.getName(); + * @author Keith Donald + * @since 3.0.1 + */ +public class ConversionServiceHandlerInterceptor extends HandlerInterceptorAdapter { + + private final ConversionService conversionService; + + /** + * Creates a new {@link ConversionServiceHandlerInterceptor}. + * @param conversionService the conversion service to export to request scope when this interceptor is invoked. + */ + public ConversionServiceHandlerInterceptor(ConversionService conversionService) { + Assert.notNull(conversionService, "The ConversionService may not be null"); + this.conversionService = conversionService; + } + + @Override + public final boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws ServletException, IOException { + request.setAttribute(ConversionService.class.getName(), this.conversionService); + return true; + } + +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/EvalTag.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/EvalTag.java index 4424428b390..9f3f4ea2938 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/EvalTag.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/EvalTag.java @@ -20,6 +20,7 @@ import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; +import javax.servlet.jsp.el.ELException; import org.springframework.beans.BeansException; import org.springframework.core.convert.ConversionService; @@ -110,7 +111,8 @@ public class EvalTag extends HtmlEscapingAwareTag { } } else { - pageContext.setAttribute(var, expression.getValue(context), scope); + Object result = expression.getValue(context); + pageContext.setAttribute(var, result, scope); } return EVAL_PAGE; } @@ -127,7 +129,7 @@ public class EvalTag extends HtmlEscapingAwareTag { private ConversionService getConversionService() { try { - return (ConversionService) this.pageContext.getRequest().getAttribute("org.springframework.core.convert.ConversionService"); + return (ConversionService) this.pageContext.getRequest().getAttribute(ConversionService.class.getName()); } catch (BeansException e) { return null; } @@ -147,19 +149,19 @@ public class EvalTag extends HtmlEscapingAwareTag { public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { - if (name.equals("pageContext")) { + Object implicitVar = resolveImplicitVariable(name); + if (implicitVar != null) { return true; } - // TODO support all other JSP implicit variables defined at http://java.sun.com/javaee/6/docs/api/javax/servlet/jsp/el/ImplicitObjectELResolver.html return this.pageContext.findAttribute(name) != null; } public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { - if (name.equals("pageContext")) { - return new TypedValue(this.pageContext); + Object implicitVar = resolveImplicitVariable(name); + if (implicitVar != null) { + return new TypedValue(implicitVar); } - // TODO support all other JSP implicit variables defined at http://java.sun.com/javaee/6/docs/api/javax/servlet/jsp/el/ImplicitObjectELResolver.html return new TypedValue(this.pageContext.findAttribute(name)); } @@ -173,6 +175,14 @@ public class EvalTag extends HtmlEscapingAwareTag { throw new UnsupportedOperationException(); } + private Object resolveImplicitVariable(String name) throws AccessException { + try { + return this.pageContext.getVariableResolver().resolveVariable(name); + } catch (ELException e) { + throw new AccessException("Unexpected exception occurred accessing '" + name + "' as an implicit variable", e); + } + } + } } \ No newline at end of file diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockPageContext.java b/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockPageContext.java index e64f93c4935..c72f9f0dc01 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockPageContext.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockPageContext.java @@ -294,7 +294,15 @@ public class MockPageContext extends PageContext { } public VariableResolver getVariableResolver() { - return null; + return new VariableResolver() { + public Object resolveVariable(String pName) throws ELException { + if (pName.equals("pageContext")) { + return this; + } else { + return null; + } + } + }; } public HttpSession getSession() { diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java index 459f066cf5a..0c6ae697000 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java @@ -53,6 +53,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.handler.ConversionServiceHandlerInterceptor; import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; @@ -80,17 +81,19 @@ public class MvcNamespaceTests { public void testDefaultConfig() throws Exception { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(appContext); reader.loadBeanDefinitions(new ClassPathResource("mvc-config.xml", getClass())); - assertEquals(4, appContext.getBeanDefinitionCount()); + assertEquals(5, appContext.getBeanDefinitionCount()); appContext.refresh(); DefaultAnnotationHandlerMapping mapping = appContext.getBean(DefaultAnnotationHandlerMapping.class); assertNotNull(mapping); assertEquals(0, mapping.getOrder()); - + TestController handler = new TestController(); + mapping.setDefaultHandler(handler); + AnnotationMethodHandlerAdapter adapter = appContext.getBean(AnnotationMethodHandlerAdapter.class); assertNotNull(adapter); - - HttpMessageConverter[] messageConverters = adapter.getMessageConverters(); + + HttpMessageConverter[] messageConverters = adapter.getMessageConverters(); assertNotNull(messageConverters); assertEquals(6, messageConverters.length); assertTrue(ByteArrayHttpMessageConverter.class.equals(messageConverters[0].getClass())); @@ -105,12 +108,18 @@ public class MvcNamespaceTests { assertNotNull(appContext.getBean(LocalValidatorFactoryBean.class)); assertNotNull(appContext.getBean(Validator.class)); - TestController handler = new TestController(); - // default web binding initializer behavior test MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter("date", "2009-10-31"); MockHttpServletResponse response = new MockHttpServletResponse(); + + HandlerExecutionChain chain = mapping.getHandler(request); + assertEquals(2, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); + ConversionServiceHandlerInterceptor interceptor = (ConversionServiceHandlerInterceptor) chain.getInterceptors()[1]; + interceptor.preHandle(request, response, handler); + assertSame(appContext.getBean(ConversionService.class), request.getAttribute(ConversionService.class.getName())); + adapter.handle(request, response, handler); assertTrue(handler.recordedValidationError); } @@ -119,18 +128,29 @@ public class MvcNamespaceTests { public void testCustomConversionService() throws Exception { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(appContext); reader.loadBeanDefinitions(new ClassPathResource("mvc-config-custom-conversion-service.xml", getClass())); - assertEquals(4, appContext.getBeanDefinitionCount()); + assertEquals(5, appContext.getBeanDefinitionCount()); appContext.refresh(); - AnnotationMethodHandlerAdapter adapter = appContext.getBean(AnnotationMethodHandlerAdapter.class); - assertNotNull(adapter); - + DefaultAnnotationHandlerMapping mapping = appContext.getBean(DefaultAnnotationHandlerMapping.class); + assertNotNull(mapping); TestController handler = new TestController(); - + mapping.setDefaultHandler(handler); + // default web binding initializer behavior test MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/accounts/12345"); request.addParameter("date", "2009-10-31"); MockHttpServletResponse response = new MockHttpServletResponse(); + + HandlerExecutionChain chain = mapping.getHandler(request); + assertEquals(2, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); + ConversionServiceHandlerInterceptor interceptor = (ConversionServiceHandlerInterceptor) chain.getInterceptors()[1]; + interceptor.preHandle(request, response, handler); + assertSame(appContext.getBean("conversionService"), request.getAttribute(ConversionService.class.getName())); + + AnnotationMethodHandlerAdapter adapter = appContext.getBean(AnnotationMethodHandlerAdapter.class); + assertNotNull(adapter); adapter.handle(request, response, handler); } @@ -138,7 +158,7 @@ public class MvcNamespaceTests { public void testCustomValidator() throws Exception { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(appContext); reader.loadBeanDefinitions(new ClassPathResource("mvc-config-custom-validator.xml", getClass())); - assertEquals(4, appContext.getBeanDefinitionCount()); + assertEquals(5, appContext.getBeanDefinitionCount()); appContext.refresh(); AnnotationMethodHandlerAdapter adapter = appContext.getBean(AnnotationMethodHandlerAdapter.class); @@ -160,7 +180,7 @@ public class MvcNamespaceTests { public void testInterceptors() throws Exception { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(appContext); reader.loadBeanDefinitions(new ClassPathResource("mvc-config-interceptors.xml", getClass())); - assertEquals(7, appContext.getBeanDefinitionCount()); + assertEquals(8, appContext.getBeanDefinitionCount()); appContext.refresh(); DefaultAnnotationHandlerMapping mapping = appContext.getBean(DefaultAnnotationHandlerMapping.class); @@ -173,19 +193,20 @@ public class MvcNamespaceTests { request.addParameter("theme", "green"); HandlerExecutionChain chain = mapping.getHandler(request); - assertEquals(3, chain.getInterceptors().length); - assertTrue(chain.getInterceptors()[1] instanceof LocaleChangeInterceptor); - assertTrue(chain.getInterceptors()[2] instanceof ThemeChangeInterceptor); + assertEquals(4, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); + assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); + assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); request.setRequestURI("/logged/accounts/12345"); chain = mapping.getHandler(request); - assertEquals(4, chain.getInterceptors().length); - assertTrue(chain.getInterceptors()[3] instanceof WebRequestHandlerInterceptorAdapter); + assertEquals(5, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[4] instanceof WebRequestHandlerInterceptorAdapter); request.setRequestURI("/foo/logged"); chain = mapping.getHandler(request); - assertEquals(4, chain.getInterceptors().length); - assertTrue(chain.getInterceptors()[3] instanceof WebRequestHandlerInterceptorAdapter); + assertEquals(5, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[4] instanceof WebRequestHandlerInterceptorAdapter); } @@ -193,7 +214,7 @@ public class MvcNamespaceTests { public void testBeanDecoration() throws Exception { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(appContext); reader.loadBeanDefinitions(new ClassPathResource("mvc-config-bean-decoration.xml", getClass())); - assertEquals(6, appContext.getBeanDefinitionCount()); + assertEquals(7, appContext.getBeanDefinitionCount()); appContext.refresh(); DefaultAnnotationHandlerMapping mapping = appContext.getBean(DefaultAnnotationHandlerMapping.class); @@ -203,11 +224,13 @@ public class MvcNamespaceTests { MockHttpServletRequest request = new MockHttpServletRequest(); HandlerExecutionChain chain = mapping.getHandler(request); - assertEquals(3, chain.getInterceptors().length); - assertTrue(chain.getInterceptors()[1] instanceof LocaleChangeInterceptor); - LocaleChangeInterceptor interceptor = (LocaleChangeInterceptor) chain.getInterceptors()[1]; + assertEquals(4, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); + assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); + assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); + LocaleChangeInterceptor interceptor = (LocaleChangeInterceptor) chain.getInterceptors()[2]; assertEquals("lang", interceptor.getParamName()); - ThemeChangeInterceptor interceptor2 = (ThemeChangeInterceptor) chain.getInterceptors()[2]; + ThemeChangeInterceptor interceptor2 = (ThemeChangeInterceptor) chain.getInterceptors()[3]; assertEquals("style", interceptor2.getParamName()); } @@ -215,7 +238,7 @@ public class MvcNamespaceTests { public void testViewControllers() throws Exception { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(appContext); reader.loadBeanDefinitions(new ClassPathResource("mvc-config-view-controllers.xml", getClass())); - assertEquals(8, appContext.getBeanDefinitionCount()); + assertEquals(9, appContext.getBeanDefinitionCount()); appContext.refresh(); DefaultAnnotationHandlerMapping mapping = appContext.getBean(DefaultAnnotationHandlerMapping.class); @@ -225,8 +248,10 @@ public class MvcNamespaceTests { MockHttpServletRequest request = new MockHttpServletRequest(); HandlerExecutionChain chain = mapping.getHandler(request); - assertEquals(3, chain.getInterceptors().length); - assertTrue(chain.getInterceptors()[1] instanceof LocaleChangeInterceptor); + assertEquals(4, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); + assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); + assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); SimpleUrlHandlerMapping mapping2 = appContext.getBean(SimpleUrlHandlerMapping.class); assertNotNull(mapping2); @@ -237,15 +262,19 @@ public class MvcNamespaceTests { request.setRequestURI("/foo"); request.setMethod("GET"); chain = mapping2.getHandler(request); - assertEquals(3, chain.getInterceptors().length); - assertTrue(chain.getInterceptors()[1] instanceof LocaleChangeInterceptor); + assertEquals(4, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); + assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); + assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); ModelAndView mv = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler()); assertNull(mv.getViewName()); request.setRequestURI("/bar"); chain = mapping2.getHandler(request); - assertEquals(3, chain.getInterceptors().length); - assertTrue(chain.getInterceptors()[1] instanceof LocaleChangeInterceptor); + assertEquals(4, chain.getInterceptors().length); + assertTrue(chain.getInterceptors()[1] instanceof ConversionServiceHandlerInterceptor); + assertTrue(chain.getInterceptors()[2] instanceof LocaleChangeInterceptor); + assertTrue(chain.getInterceptors()[3] instanceof ThemeChangeInterceptor); ModelAndView mv2 = adapter.handle(request, new MockHttpServletResponse(), chain.getHandler()); assertEquals("baz", mv2.getViewName()); } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/EvalTagTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/EvalTagTests.java index d386c981cba..3b0bd2dcc33 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/EvalTagTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/EvalTagTests.java @@ -42,7 +42,7 @@ public class EvalTagTests extends AbstractTagTests { tag.setPageContext(context); } - public void testEndTagPrintScopedAttributeResult() throws Exception { + public void testPrintScopedAttributeResult() throws Exception { tag.setExpression("bean.method()"); int action = tag.doStartTag(); assertEquals(Tag.EVAL_BODY_INCLUDE, action); @@ -51,14 +51,43 @@ public class EvalTagTests extends AbstractTagTests { assertEquals("foo", ((MockHttpServletResponse)context.getResponse()).getContentAsString()); } - public void testEndTagPrintFormattedScopedAttributeResult() throws Exception { + public void testPrintFormattedScopedAttributeResult() throws Exception { tag.setExpression("bean.formattable"); int action = tag.doStartTag(); assertEquals(Tag.EVAL_BODY_INCLUDE, action); action = tag.doEndTag(); assertEquals(Tag.EVAL_PAGE, action); - assertEquals("25%", ((MockHttpServletResponse) context.getResponse()) - .getContentAsString()); + assertEquals("25%", ((MockHttpServletResponse) context.getResponse()).getContentAsString()); + } + + public void testPrintHtmlEscapedAttributeResult() throws Exception { + tag.setExpression("bean.html()"); + tag.setHtmlEscape("true"); + int action = tag.doStartTag(); + assertEquals(Tag.EVAL_BODY_INCLUDE, action); + action = tag.doEndTag(); + assertEquals(Tag.EVAL_PAGE, action); + assertEquals("<p>", ((MockHttpServletResponse)context.getResponse()).getContentAsString()); + } + + public void testPrintJavaScriptEscapedAttributeResult() throws Exception { + tag.setExpression("bean.js()"); + tag.setJavaScriptEscape("true"); + int action = tag.doStartTag(); + assertEquals(Tag.EVAL_BODY_INCLUDE, action); + action = tag.doEndTag(); + assertEquals(Tag.EVAL_PAGE, action); + assertEquals("function foo() { alert(\\\"hi\\\") }", ((MockHttpServletResponse)context.getResponse()).getContentAsString()); + } + + public void testSetFormattedScopedAttributeResult() throws Exception { + tag.setExpression("bean.formattable"); + tag.setVar("foo"); + int action = tag.doStartTag(); + assertEquals(Tag.EVAL_BODY_INCLUDE, action); + action = tag.doEndTag(); + assertEquals(Tag.EVAL_PAGE, action); + assertEquals(new BigDecimal(".25"), context.getAttribute("foo")); } public static class Bean { @@ -71,5 +100,13 @@ public class EvalTagTests extends AbstractTagTests { public BigDecimal getFormattable() { return new BigDecimal(".25"); } + + public String html() { + return "

"; + } + + public String js() { + return "function foo() { alert(\"hi\") }"; + } } }