Skip to content

Commit f9b17a7

Browse files
beamerblvdphilwebb
authored andcommitted
Add <spring:argument> subtag for message/theme
Add a new <spring:argument> tag that cab be nested within <spring:message> and <spring:theme>. The tag is based on the <fmt:param> tag and uses conventions found throughout other Spring tags. Issue: SPR-9678
1 parent 50bd70f commit f9b17a7

File tree

8 files changed

+396
-11
lines changed

8 files changed

+396
-11
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2002-2013 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 javax.servlet.jsp.JspTagException;
20+
21+
/**
22+
* Allows implementing tag to utilize nested {@code spring:argument} tags.
23+
*
24+
* @author Nicholas Williams
25+
* @since 4.0
26+
* @see ArgumentTag
27+
*/
28+
public interface ArgumentAware {
29+
30+
/**
31+
* Callback hook for nested spring:argument tags to pass their value
32+
* to the parent tag.
33+
* @param argument the result of the nested {@code spring:argument} tag
34+
*/
35+
void addArgument(Object argument) throws JspTagException;
36+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2002-2013 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 javax.servlet.jsp.JspException;
20+
import javax.servlet.jsp.tagext.BodyTagSupport;
21+
22+
/**
23+
* JSP tag for collecting arguments and passing them to an {@link ArgumentAware} ancestor
24+
* in the tag hierarchy.
25+
*
26+
* <p>This tag must be nested under an argument aware tag.
27+
*
28+
* @author Nicholas Williams
29+
* @since 4.0
30+
* @see MessageTag
31+
* @see ThemeTag
32+
*/
33+
@SuppressWarnings("serial")
34+
public class ArgumentTag extends BodyTagSupport {
35+
36+
private Object value;
37+
38+
private boolean valueSet;
39+
40+
// tag lifecycle
41+
42+
@Override
43+
public int doEndTag() throws JspException {
44+
Object argument = null;
45+
if (this.valueSet) {
46+
argument = this.value;
47+
}
48+
else if (getBodyContent() != null) {
49+
// get the value from the tag body
50+
argument = getBodyContent().getString().trim();
51+
}
52+
53+
// find a param aware ancestor
54+
ArgumentAware argumentAwareTag = (ArgumentAware) findAncestorWithClass(this,
55+
ArgumentAware.class);
56+
if (argumentAwareTag == null) {
57+
throw new JspException(
58+
"The argument tag must be a descendant of a tag that supports arguments");
59+
}
60+
61+
argumentAwareTag.addArgument(argument);
62+
63+
return EVAL_PAGE;
64+
}
65+
66+
// tag attribute mutators
67+
68+
/**
69+
* Sets the value of the argument
70+
*
71+
* <p>
72+
* Optional. If not set, the tag's body content is evaluated.
73+
*
74+
* @param value the parameter value
75+
*/
76+
public void setValue(Object value) {
77+
this.value = value;
78+
this.valueSet = true;
79+
}
80+
81+
@Override
82+
public void release() {
83+
super.release();
84+
this.value = null;
85+
this.valueSet = false;
86+
}
87+
88+
}

spring-webmvc/src/main/java/org/springframework/web/servlet/tags/MessageTag.java

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
import java.io.IOException;
2020
import java.util.Collection;
21+
import java.util.LinkedList;
22+
import java.util.List;
23+
2124
import javax.servlet.jsp.JspException;
2225
import javax.servlet.jsp.JspTagException;
2326

@@ -40,17 +43,22 @@
4043
* <p>If "code" isn't set or cannot be resolved, "text" will be used as default
4144
* message. Thus, this tag can also be used for HTML escaping of any texts.
4245
*
46+
* <p>Message arguments can be specified via the {@link #setArguments(Object) arguments}
47+
* attribute or by using nested {@code &lt;spring:argument&gt;} tags.
48+
*
4349
* @author Rod Johnson
4450
* @author Juergen Hoeller
51+
* @author Nicholas Williams
4552
* @see #setCode
4653
* @see #setText
4754
* @see #setHtmlEscape
4855
* @see #setJavaScriptEscape
4956
* @see HtmlEscapeTag#setDefaultHtmlEscape
5057
* @see org.springframework.web.util.WebUtils#HTML_ESCAPE_CONTEXT_PARAM
58+
* @see ArgumentTag
5159
*/
5260
@SuppressWarnings("serial")
53-
public class MessageTag extends HtmlEscapingAwareTag {
61+
public class MessageTag extends HtmlEscapingAwareTag implements ArgumentAware {
5462

5563
/**
5664
* Default separator for splitting an arguments String: a comma (",")
@@ -66,6 +74,8 @@ public class MessageTag extends HtmlEscapingAwareTag {
6674

6775
private String argumentSeparator = DEFAULT_ARGUMENT_SEPARATOR;
6876

77+
private List<Object> nestedArguments;
78+
6979
private String text;
7080

7181
private String var;
@@ -109,6 +119,11 @@ public void setArgumentSeparator(String argumentSeparator) {
109119
this.argumentSeparator = argumentSeparator;
110120
}
111121

122+
@Override
123+
public void addArgument(Object argument) throws JspTagException {
124+
this.nestedArguments.add(argument);
125+
}
126+
112127
/**
113128
* Set the message text for this tag.
114129
*/
@@ -146,6 +161,12 @@ public void setJavaScriptEscape(boolean javaScriptEscape) throws JspException {
146161
}
147162

148163

164+
@Override
165+
protected final int doStartTagInternal() throws JspException, IOException {
166+
this.nestedArguments = new LinkedList<Object>();
167+
return EVAL_BODY_INCLUDE;
168+
}
169+
149170
/**
150171
* Resolves the message, escapes it if demanded,
151172
* and writes it to the page (or exposes it as variable).
@@ -155,7 +176,7 @@ public void setJavaScriptEscape(boolean javaScriptEscape) throws JspException {
155176
* @see #writeMessage(String)
156177
*/
157178
@Override
158-
protected final int doStartTagInternal() throws JspException, IOException {
179+
public int doEndTag() throws JspException {
159180
try {
160181
// Resolve the unescaped message.
161182
String msg = resolveMessage();
@@ -172,13 +193,22 @@ protected final int doStartTagInternal() throws JspException, IOException {
172193
writeMessage(msg);
173194
}
174195

175-
return EVAL_BODY_INCLUDE;
196+
return EVAL_PAGE;
197+
}
198+
catch (IOException ex) {
199+
throw new JspTagException(ex.getMessage(), ex);
176200
}
177201
catch (NoSuchMessageException ex) {
178202
throw new JspTagException(getNoSuchMessageExceptionDescription(ex));
179203
}
180204
}
181205

206+
@Override
207+
public void release() {
208+
super.release();
209+
this.arguments = null;
210+
}
211+
182212
/**
183213
* Resolve the specified message into a concrete message String.
184214
* The returned message String should be unescaped.
@@ -198,6 +228,11 @@ protected String resolveMessage() throws JspException, NoSuchMessageException {
198228
if (this.code != null || this.text != null) {
199229
// We have a code or default text that we need to resolve.
200230
Object[] argumentsArray = resolveArguments(this.arguments);
231+
if(!this.nestedArguments.isEmpty()) {
232+
argumentsArray = appendArguments(argumentsArray,
233+
this.nestedArguments.toArray());
234+
}
235+
201236
if (this.text != null) {
202237
// We have a fallback text to consider.
203238
return messageSource.getMessage(
@@ -214,6 +249,16 @@ protected String resolveMessage() throws JspException, NoSuchMessageException {
214249
return this.text;
215250
}
216251

252+
private Object[] appendArguments(Object[] sourceArguments, Object[] additionalArguments) {
253+
if(ObjectUtils.isEmpty(sourceArguments)) {
254+
return additionalArguments;
255+
}
256+
Object[] arguments = new Object[sourceArguments.length + additionalArguments.length];
257+
System.arraycopy(sourceArguments, 0, arguments, 0, sourceArguments.length);
258+
System.arraycopy(additionalArguments, 0, arguments, sourceArguments.length, additionalArguments.length);
259+
return arguments;
260+
}
261+
217262
/**
218263
* Resolve the given arguments Object into an arguments array.
219264
* @param arguments the specified arguments Object

spring-webmvc/src/main/java/org/springframework/web/servlet/tags/ThemeTag.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,9 @@
3030
* <p>If "code" isn't set or cannot be resolved, "text" will be used
3131
* as default message.
3232
*
33+
* <p>Message arguments can be specified via the {@link #setArguments(Object) arguments}
34+
* attribute or by using nested {@code &lt;spring:argument&gt;} tags.
35+
*
3336
* @author Jean-Pierre Pawlak
3437
* @author Juergen Hoeller
3538
* @see org.springframework.ui.context.Theme
@@ -39,6 +42,7 @@
3942
* @see #setHtmlEscape
4043
* @see HtmlEscapeTag#setDefaultHtmlEscape
4144
* @see org.springframework.web.util.WebUtils#HTML_ESCAPE_CONTEXT_PARAM
45+
* @see ArgumentTag
4246
*/
4347
@SuppressWarnings("serial")
4448
public class ThemeTag extends MessageTag {

spring-webmvc/src/main/resources/META-INF/spring.tld

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@
8383
<description>Set optional message arguments for this tag, as a
8484
(comma-)delimited String (each String argument can contain JSP EL),
8585
an Object array (used as argument array), or a single Object (used
86-
as single argument).</description>
86+
as single argument). You can additionally use nested spring:argument
87+
tags.</description>
8788
<name>arguments</name>
8889
<required>false</required>
8990
<rtexprvalue>true</rtexprvalue>
@@ -160,7 +161,8 @@
160161
<description>Set optional message arguments for this tag, as a
161162
(comma-)delimited String (each String argument can contain JSP EL),
162163
an Object array (used as argument array), or a single Object (used
163-
as single argument).</description>
164+
as single argument). You can additionally use nested spring:argument
165+
tags.</description>
164166
<name>arguments</name>
165167
<required>false</required>
166168
<rtexprvalue>true</rtexprvalue>
@@ -211,6 +213,22 @@
211213
</attribute>
212214
</tag>
213215

216+
<tag>
217+
<description>Argument tag based on the JSTL fmt:param tag. The purpose is to
218+
support arguments inside the spring:message and spring:theme
219+
tags.</description>
220+
<name>argument</name>
221+
<tag-class>org.springframework.web.servlet.tags.ArgumentTag</tag-class>
222+
<body-content>JSP</body-content>
223+
<attribute>
224+
<description>The value of the argument.</description>
225+
<name>value</name>
226+
<required>false</required>
227+
<rtexprvalue>true</rtexprvalue>
228+
<type>java.lang.Object</type>
229+
</attribute>
230+
</tag>
231+
214232
<tag>
215233
<description>
216234
Provides Errors instance in case of bind errors.

0 commit comments

Comments
 (0)