From f23b55dc132482300e7962c489a591005550cb33 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Thu, 11 Feb 2010 22:53:49 +0000 Subject: [PATCH] spring:eval tag initial commit --- .../core/convert/TypeDescriptor.java | 5 +- .../support/GenericConversionService.java | 7 +- org.springframework.web.servlet/.classpath | 1 + org.springframework.web.servlet/ivy.xml | 2 + .../web/servlet/tags/EvalTag.java | 181 ++++++++++++++++++ .../src/main/resources/META-INF/spring.tld | 44 ++++- .../web/servlet/tags/AbstractTagTests.java | 10 +- .../web/servlet/tags/EvalTagTests.java | 71 +++++++ 8 files changed, 309 insertions(+), 12 deletions(-) create mode 100644 org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/EvalTag.java create mode 100644 org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/EvalTagTests.java diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 5258c95909..0e1f71a55f 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -439,16 +439,17 @@ public class TypeDescriptor { public String toString() { if (this == TypeDescriptor.NULL) { - return "TypeDescriptor.NULL"; + return "[TypeDescriptor.NULL]"; } else { StringBuilder builder = new StringBuilder(); - builder.append("TypeDescriptor "); + builder.append("[TypeDescriptor "); Annotation[] anns = getAnnotations(); for (Annotation ann : anns) { builder.append("@").append(ann.annotationType().getName()).append(' '); } builder.append(ClassUtils.getQualifiedName(getType())); + builder.append("]"); return builder.toString(); } } diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index b025bee802..588498217b 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -28,7 +28,6 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.core.GenericTypeResolver; import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.ConversionService; @@ -39,6 +38,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.core.convert.converter.GenericConverter; +import org.springframework.core.style.StylerUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -121,9 +121,12 @@ public class GenericConversionService implements ConversionService, ConverterReg public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { assertNotNull(sourceType, targetType); + if (logger.isDebugEnabled()) { + logger.debug("Converting value " + StylerUtils.style(source) +" of " + sourceType + " to " + targetType); + } if (sourceType == TypeDescriptor.NULL) { Assert.isTrue(source == null, "The source must be null if sourceType == TypeDescriptor.NULL"); - return convertNullSource(sourceType, targetType); + return convertNullSource(sourceType, targetType); } if (targetType == TypeDescriptor.NULL) { return null; diff --git a/org.springframework.web.servlet/.classpath b/org.springframework.web.servlet/.classpath index 1584462c12..e241d01b08 100644 --- a/org.springframework.web.servlet/.classpath +++ b/org.springframework.web.servlet/.classpath @@ -50,5 +50,6 @@ + diff --git a/org.springframework.web.servlet/ivy.xml b/org.springframework.web.servlet/ivy.xml index 3aa42c9bc1..47bbebf752 100644 --- a/org.springframework.web.servlet/ivy.xml +++ b/org.springframework.web.servlet/ivy.xml @@ -72,6 +72,8 @@ conf="optional, velocity, freemarker, jasper-reports->compile"/> + [] getSpecificTargetClasses() { + return null; + } + + public boolean canRead(EvaluationContext context, Object target, + String name) throws AccessException { + if (name.equals("pageContext")) { + 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); + } + // 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)); + } + + public boolean canWrite(EvaluationContext context, Object target, + String name) throws AccessException { + return false; + } + + public void write(EvaluationContext context, Object target, + String name, Object newValue) throws AccessException { + throw new UnsupportedOperationException(); + } + + } + +} \ No newline at end of file diff --git a/org.springframework.web.servlet/src/main/resources/META-INF/spring.tld b/org.springframework.web.servlet/src/main/resources/META-INF/spring.tld index 8ac57e7b04..74f3c460bd 100644 --- a/org.springframework.web.servlet/src/main/resources/META-INF/spring.tld +++ b/org.springframework.web.servlet/src/main/resources/META-INF/spring.tld @@ -358,8 +358,8 @@ true - Specifies a remote application context. The default is the - current application context. + Specifies a remote application context path. The default is the + current application context path. context false true @@ -414,4 +414,44 @@ + + Evaluates a Spring expression (SpEL) and either prints the result or assigns it to a variable. + eval + org.springframework.web.servlet.tags.EvalTag + JSP + + The expression to evaluate. + expression + true + true + + + The name of the variable to export the evaluation result to. + var + false + true + + + The scope for the var. 'application', 'session', 'request' and + 'page' scopes are supported. Defaults to page scope. This attribute has no + effect unless the var attribute is also defined. + scope + false + true + + + Set HTML escaping for this tag, as boolean value. Overrides the + default HTML escaping setting for the current page. + htmlEscape + false + true + + + Set JavaScript escaping for this tag, as boolean value. Default is false. + javaScriptEscape + false + true + + + \ No newline at end of file diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/AbstractTagTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/AbstractTagTests.java index 59d76364aa..bc2cc8b710 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/AbstractTagTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/AbstractTagTests.java @@ -16,17 +16,13 @@ package org.springframework.web.servlet.tags; -import java.io.StringWriter; - -import javax.servlet.jsp.JspWriter; - import junit.framework.TestCase; -import org.springframework.mock.web.MockBodyContent; +import org.springframework.format.support.FormattingConversionServiceFactoryBean; import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockPageContext; import org.springframework.mock.web.MockServletContext; -import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.LocaleResolver; @@ -48,6 +44,8 @@ public abstract class AbstractTagTests extends TestCase { SimpleWebApplicationContext wac = new SimpleWebApplicationContext(); wac.setServletContext(sc); wac.setNamespace("test"); + // TODO this name index leads to brittle lookup by EvalTag + wac.registerSingleton("conversionService", FormattingConversionServiceFactoryBean.class); wac.refresh(); MockHttpServletRequest request = new MockHttpServletRequest(sc); 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 new file mode 100644 index 0000000000..e415904240 --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/EvalTagTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 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.tags; + +import java.math.BigDecimal; + +import javax.servlet.jsp.tagext.Tag; + +import org.springframework.format.annotation.NumberFormat; +import org.springframework.format.annotation.NumberFormat.Style; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockPageContext; + +public class EvalTagTests extends AbstractTagTests { + + private EvalTag tag; + + private MockPageContext context; + + protected void setUp() throws Exception { + context = createPageContext(); + context.getRequest().setAttribute("bean", new Bean()); + tag = new EvalTag(); + tag.setPageContext(context); + } + + public void testEndTagPrintScopedAttributeResult() throws Exception { + tag.setExpression("bean.method()"); + int action = tag.doStartTag(); + assertEquals(Tag.EVAL_BODY_INCLUDE, action); + action = tag.doEndTag(); + assertEquals(Tag.EVAL_PAGE, action); + assertEquals("foo", ((MockHttpServletResponse)context.getResponse()).getContentAsString()); + } + + public void testEndTagPrintFormattedScopedAttributeResult() throws Exception { + tag.setExpression("bean.formattable"); + int action = tag.doStartTag(); + assertEquals(Tag.EVAL_BODY_INCLUDE, action); + action = tag.doEndTag(); + assertEquals(Tag.EVAL_PAGE, action); + // TODO - fails because EL does not consider annotations on getter/setter method or field for properties (just annotations on method parameters) + //assertEquals("25%", ((MockHttpServletResponse)context.getResponse()).getContentAsString()); + } + + public static class Bean { + + public String method() { + return "foo"; + } + + @NumberFormat(style=Style.PERCENT) + public BigDecimal getFormattable() { + return new BigDecimal(".25"); + } + } +} -- GitLab