Skip to content

Commit f23b55d

Browse files
author
Keith Donald
committed
spring:eval tag initial commit
1 parent 16aa399 commit f23b55d

File tree

8 files changed

+309
-12
lines changed

8 files changed

+309
-12
lines changed

org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,16 +439,17 @@ else if (isMap()) {
439439

440440
public String toString() {
441441
if (this == TypeDescriptor.NULL) {
442-
return "TypeDescriptor.NULL";
442+
return "[TypeDescriptor.NULL]";
443443
}
444444
else {
445445
StringBuilder builder = new StringBuilder();
446-
builder.append("TypeDescriptor ");
446+
builder.append("[TypeDescriptor ");
447447
Annotation[] anns = getAnnotations();
448448
for (Annotation ann : anns) {
449449
builder.append("@").append(ann.annotationType().getName()).append(' ');
450450
}
451451
builder.append(ClassUtils.getQualifiedName(getType()));
452+
builder.append("]");
452453
return builder.toString();
453454
}
454455
}

org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
import org.apache.commons.logging.Log;
3030
import org.apache.commons.logging.LogFactory;
31-
3231
import org.springframework.core.GenericTypeResolver;
3332
import org.springframework.core.convert.ConversionFailedException;
3433
import org.springframework.core.convert.ConversionService;
@@ -39,6 +38,7 @@
3938
import org.springframework.core.convert.converter.ConverterFactory;
4039
import org.springframework.core.convert.converter.ConverterRegistry;
4140
import org.springframework.core.convert.converter.GenericConverter;
41+
import org.springframework.core.style.StylerUtils;
4242
import org.springframework.util.Assert;
4343
import org.springframework.util.ClassUtils;
4444

@@ -121,9 +121,12 @@ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType)
121121

122122
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
123123
assertNotNull(sourceType, targetType);
124+
if (logger.isDebugEnabled()) {
125+
logger.debug("Converting value " + StylerUtils.style(source) +" of " + sourceType + " to " + targetType);
126+
}
124127
if (sourceType == TypeDescriptor.NULL) {
125128
Assert.isTrue(source == null, "The source must be null if sourceType == TypeDescriptor.NULL");
126-
return convertNullSource(sourceType, targetType);
129+
return convertNullSource(sourceType, targetType);
127130
}
128131
if (targetType == TypeDescriptor.NULL) {
129132
return null;

org.springframework.web.servlet/.classpath

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,6 @@
5050
<classpathentry kind="var" path="IVY_CACHE/javax.validation/com.springsource.javax.validation/1.0.0.GA/com.springsource.javax.validation-1.0.0.GA.jar" sourcepath="/IVY_CACHE/javax.validation/com.springsource.javax.validation/1.0.0/com.springsource.javax.validation-sources-1.0.0.GA.jar"/>
5151
<classpathentry kind="var" path="IVY_CACHE/org.slf4j/com.springsource.slf4j.jcl/1.5.3/com.springsource.slf4j.jcl-1.5.3.jar" sourcepath="/IVY_CACHE/org.slf4j/com.springsource.slf4j.jcl/1.5.3/com.springsource.slf4j.jcl-sources-1.5.3.jar"/>
5252
<classpathentry kind="var" path="IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.3/com.springsource.slf4j.api-1.5.3.jar" sourcepath="/IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.3/com.springsource.slf4j.api-sources-1.5.3.jar"/>
53+
<classpathentry combineaccessrules="false" kind="src" path="/org.springframework.expression"/>
5354
<classpathentry kind="output" path="target/classes"/>
5455
</classpath>

org.springframework.web.servlet/ivy.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272
conf="optional, velocity, freemarker, jasper-reports->compile"/>
7373
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration"
7474
conf="compile->compile"/>
75+
<dependency org="org.springframework" name="org.springframework.expression" rev="latest.integration"
76+
conf="compile->compile"/>
7577
<dependency org="org.springframework" name="org.springframework.oxm" rev="latest.integration"
7678
conf="optional, oxm->compile"/>
7779
<dependency org="org.springframework" name="org.springframework.web" rev="latest.integration"
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*
2+
* Copyright 2002-2009 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.servlet.tags;
18+
19+
import java.io.IOException;
20+
21+
import javax.servlet.jsp.JspException;
22+
import javax.servlet.jsp.PageContext;
23+
24+
import org.springframework.beans.BeansException;
25+
import org.springframework.core.convert.ConversionService;
26+
import org.springframework.expression.AccessException;
27+
import org.springframework.expression.EvaluationContext;
28+
import org.springframework.expression.Expression;
29+
import org.springframework.expression.ExpressionParser;
30+
import org.springframework.expression.PropertyAccessor;
31+
import org.springframework.expression.TypedValue;
32+
import org.springframework.expression.spel.standard.SpelExpressionParser;
33+
import org.springframework.expression.spel.support.StandardEvaluationContext;
34+
import org.springframework.expression.spel.support.StandardTypeConverter;
35+
import org.springframework.web.util.ExpressionEvaluationUtils;
36+
import org.springframework.web.util.HtmlUtils;
37+
import org.springframework.web.util.JavaScriptUtils;
38+
import org.springframework.web.util.TagUtils;
39+
40+
/**
41+
* JSP tag for evaluating expressions with the Spring Expression Language (SpEL).
42+
* Supports the standard JSP evaluation context consisting of implicit variables and scoped attributes.
43+
*
44+
* @author Keith Donald
45+
* @since 3.0.1
46+
*/
47+
public class EvalTag extends HtmlEscapingAwareTag {
48+
49+
private ExpressionParser expressionParser;
50+
51+
private String expression;
52+
53+
private String var;
54+
55+
private int scope = PageContext.PAGE_SCOPE;
56+
57+
private boolean javaScriptEscape = false;
58+
59+
/**
60+
* Set the expression to evaluate.
61+
*/
62+
public void setExpression(String expression) {
63+
this.expression = expression;
64+
}
65+
66+
/**
67+
* Set the variable name to expose the evaluation result under.
68+
* Defaults to rendering the result to the current JspWriter
69+
*/
70+
public void setVar(String var) {
71+
this.var = var;
72+
}
73+
74+
/**
75+
* Set the scope to export the evaluation result to.
76+
* This attribute has no meaning unless var is also defined.
77+
*/
78+
public void setScope(String scope) {
79+
this.scope = TagUtils.getScope(scope);
80+
}
81+
82+
/**
83+
* Set JavaScript escaping for this tag, as boolean value.
84+
* Default is "false".
85+
*/
86+
public void setJavaScriptEscape(String javaScriptEscape) throws JspException {
87+
this.javaScriptEscape =
88+
ExpressionEvaluationUtils.evaluateBoolean("javaScriptEscape", javaScriptEscape, this.pageContext);
89+
}
90+
91+
@Override
92+
public int doStartTagInternal() throws JspException {
93+
this.expressionParser = new SpelExpressionParser();
94+
return EVAL_BODY_INCLUDE;
95+
}
96+
97+
@Override
98+
public int doEndTag() throws JspException {
99+
Expression expression = this.expressionParser.parseExpression(this.expression);
100+
EvaluationContext context = createEvaluationContext();
101+
if (this.var == null) {
102+
// print the url to the writer
103+
try {
104+
String result = expression.getValue(context, String.class);
105+
result = isHtmlEscape() ? HtmlUtils.htmlEscape(result) : result;
106+
result = this.javaScriptEscape ? JavaScriptUtils.javaScriptEscape(result) : result;
107+
pageContext.getOut().print(result);
108+
}
109+
catch (IOException e) {
110+
throw new JspException(e);
111+
}
112+
}
113+
else {
114+
// store the url as a variable
115+
pageContext.setAttribute(var, expression.getValue(context), scope);
116+
}
117+
return EVAL_PAGE;
118+
}
119+
120+
private EvaluationContext createEvaluationContext() {
121+
StandardEvaluationContext context = new StandardEvaluationContext();
122+
context.addPropertyAccessor(new JspPropertyAccessor(this.pageContext));
123+
ConversionService conversionService = getConversionService();
124+
if (conversionService != null) {
125+
context.setTypeConverter(new StandardTypeConverter());
126+
}
127+
return context;
128+
}
129+
130+
private ConversionService getConversionService() {
131+
try {
132+
// TODO replace this with a call to RequestContext that is not brittle
133+
return getRequestContext().getWebApplicationContext().getBean("conversionService", ConversionService.class);
134+
} catch (BeansException e) {
135+
return null;
136+
}
137+
}
138+
139+
private static class JspPropertyAccessor implements PropertyAccessor {
140+
141+
private PageContext pageContext;
142+
143+
public JspPropertyAccessor(PageContext pageContext) {
144+
this.pageContext = pageContext;
145+
}
146+
147+
public Class<?>[] getSpecificTargetClasses() {
148+
return null;
149+
}
150+
151+
public boolean canRead(EvaluationContext context, Object target,
152+
String name) throws AccessException {
153+
if (name.equals("pageContext")) {
154+
return true;
155+
}
156+
// TODO support all other JSP implicit variables defined at http://java.sun.com/javaee/6/docs/api/javax/servlet/jsp/el/ImplicitObjectELResolver.html
157+
return this.pageContext.findAttribute(name) != null;
158+
}
159+
160+
public TypedValue read(EvaluationContext context, Object target,
161+
String name) throws AccessException {
162+
if (name.equals("pageContext")) {
163+
return new TypedValue(this.pageContext);
164+
}
165+
// TODO support all other JSP implicit variables defined at http://java.sun.com/javaee/6/docs/api/javax/servlet/jsp/el/ImplicitObjectELResolver.html
166+
return new TypedValue(this.pageContext.findAttribute(name));
167+
}
168+
169+
public boolean canWrite(EvaluationContext context, Object target,
170+
String name) throws AccessException {
171+
return false;
172+
}
173+
174+
public void write(EvaluationContext context, Object target,
175+
String name, Object newValue) throws AccessException {
176+
throw new UnsupportedOperationException();
177+
}
178+
179+
}
180+
181+
}

org.springframework.web.servlet/src/main/resources/META-INF/spring.tld

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,8 +358,8 @@
358358
<rtexprvalue>true</rtexprvalue>
359359
</attribute>
360360
<attribute>
361-
<description>Specifies a remote application context. The default is the
362-
current application context.</description>
361+
<description>Specifies a remote application context path. The default is the
362+
current application context path.</description>
363363
<name>context</name>
364364
<required>false</required>
365365
<rtexprvalue>true</rtexprvalue>
@@ -414,4 +414,44 @@
414414
</attribute>
415415
</tag>
416416

417+
<tag>
418+
<description>Evaluates a Spring expression (SpEL) and either prints the result or assigns it to a variable.</description>
419+
<name>eval</name>
420+
<tag-class>org.springframework.web.servlet.tags.EvalTag</tag-class>
421+
<body-content>JSP</body-content>
422+
<attribute>
423+
<description>The expression to evaluate.</description>
424+
<name>expression</name>
425+
<required>true</required>
426+
<rtexprvalue>true</rtexprvalue>
427+
</attribute>
428+
<attribute>
429+
<description>The name of the variable to export the evaluation result to.</description>
430+
<name>var</name>
431+
<required>false</required>
432+
<rtexprvalue>true</rtexprvalue>
433+
</attribute>
434+
<attribute>
435+
<description>The scope for the var. 'application', 'session', 'request' and
436+
'page' scopes are supported. Defaults to page scope. This attribute has no
437+
effect unless the var attribute is also defined.</description>
438+
<name>scope</name>
439+
<required>false</required>
440+
<rtexprvalue>true</rtexprvalue>
441+
</attribute>
442+
<attribute>
443+
<description>Set HTML escaping for this tag, as boolean value. Overrides the
444+
default HTML escaping setting for the current page.</description>
445+
<name>htmlEscape</name>
446+
<required>false</required>
447+
<rtexprvalue>true</rtexprvalue>
448+
</attribute>
449+
<attribute>
450+
<description>Set JavaScript escaping for this tag, as boolean value. Default is false.</description>
451+
<name>javaScriptEscape</name>
452+
<required>false</required>
453+
<rtexprvalue>true</rtexprvalue>
454+
</attribute>
455+
</tag>
456+
417457
</taglib>

org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/AbstractTagTests.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,13 @@
1616

1717
package org.springframework.web.servlet.tags;
1818

19-
import java.io.StringWriter;
20-
21-
import javax.servlet.jsp.JspWriter;
22-
2319
import junit.framework.TestCase;
2420

25-
import org.springframework.mock.web.MockBodyContent;
21+
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
2622
import org.springframework.mock.web.MockHttpServletRequest;
23+
import org.springframework.mock.web.MockHttpServletResponse;
2724
import org.springframework.mock.web.MockPageContext;
2825
import org.springframework.mock.web.MockServletContext;
29-
import org.springframework.mock.web.MockHttpServletResponse;
3026
import org.springframework.web.context.WebApplicationContext;
3127
import org.springframework.web.servlet.DispatcherServlet;
3228
import org.springframework.web.servlet.LocaleResolver;
@@ -48,6 +44,8 @@ protected MockPageContext createPageContext() {
4844
SimpleWebApplicationContext wac = new SimpleWebApplicationContext();
4945
wac.setServletContext(sc);
5046
wac.setNamespace("test");
47+
// TODO this name index leads to brittle lookup by EvalTag
48+
wac.registerSingleton("conversionService", FormattingConversionServiceFactoryBean.class);
5149
wac.refresh();
5250

5351
MockHttpServletRequest request = new MockHttpServletRequest(sc);

0 commit comments

Comments
 (0)