diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/Param.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/Param.java new file mode 100644 index 00000000000..b0adb36dddb --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/Param.java @@ -0,0 +1,66 @@ +/* + * 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; + +/** + * Bean used to pass name-value pair parameters from a {@link ParamTag} to a + * {@link ParamAware} tag. + * + * @author Scott Andrews + * @since 3.0 + * @see ParamTag + */ +public class Param { + + private String name; + + private String value; + + /** + * @return the non-encoded parameter name + */ + public String getName() { + return name; + } + + /** + * Set the non-encoded name of the parameter + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the non-encoded parameter value + */ + public String getValue() { + return value; + } + + /** + * Set the non-encoded value of the parameter + */ + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "JSP Tag Param: name '" + name + "', value '" + value + "'"; + } + +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/ParamAware.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/ParamAware.java new file mode 100644 index 00000000000..b4c4fb505e1 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/ParamAware.java @@ -0,0 +1,36 @@ +/* + * 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; + +/** + * Allows implementing tag to utilize nested spring:param tags. + * + * @author Scott Andrews + * @since 3.0 + * @see ParamTag + */ +public interface ParamAware { + + /** + * Callback hook for nested spring:param tags to pass their value to the + * parent tag. + * + * @param param the result of the nested spring:param tag + */ + public void addParam(Param param); + +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/ParamTag.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/ParamTag.java new file mode 100644 index 00000000000..055c437344d --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/ParamTag.java @@ -0,0 +1,95 @@ +/* + * 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 javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.BodyTagSupport; + +/** + * JSP tag for collecting name-value parameters and passing them to a + * {@link ParamAware} ancestor in the tag hierarchy. + * + *

+ * This tag must be nested under a param aware tag. + * + * @author Scott Andrews + * @since 3.0 + * @see Param + * @see UrlTag + */ +public class ParamTag extends BodyTagSupport { + + private String name; + + private String value; + + private Param param; + + // tag lifecycle + + @Override + public int doEndTag() throws JspException { + param = new Param(); + param.setName(name); + if (value != null) { + param.setValue(value); + } + else if (getBodyContent() != null) { + // get the value from the tag body + param.setValue(getBodyContent().getString().trim()); + } + + // find a param aware ancestor + ParamAware paramAwareTag = (ParamAware) findAncestorWithClass(this, + ParamAware.class); + if (paramAwareTag == null) { + throw new JspException( + "The param tag must be a descendant of a tag that supports parameters"); + } + + paramAwareTag.addParam(param); + + return EVAL_PAGE; + } + + // tag attribute accessors + + /** + * Sets the name of the parameter + * + *

+ * Required + * + * @param name the parameter name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the value of the parameter + * + *

+ * Optional. If not set, the tag's body content is evaluated + * + * @param value the parameter value + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/UrlTag.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/UrlTag.java new file mode 100644 index 00000000000..fb60775f038 --- /dev/null +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/UrlTag.java @@ -0,0 +1,371 @@ +/* + * 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.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.PageContext; +import javax.servlet.jsp.tagext.TagSupport; + +import org.springframework.util.StringUtils; +import org.springframework.web.util.TagUtils; + +/** + * JSP tag for creating URLs. Modeled after the JSTL c:url tag with backwards + * compatibility in mind. + * + *

+ * Enhancements to the JSTL functionality include: + *

+ * + *

+ * Template URI variables are indicated in the {@link #setValue(String) 'value'} + * attribute and marked by braces '{}'. The braces and attribute name are + * replaced by the URL encoded value of a parameter. If no parameter is + * available the literal value is passed through. Params matched to template + * variables will not be added to the query string. + * + *

+ * URLs can be XML escaped by setting the {@link #setEscapeXml(String) + * 'escapeXml'} attribute to 'true', the default is 'false'. + * + * @author Scott Andrews + * @since 3.0 + * @see ParamTag + */ +public class UrlTag extends TagSupport implements ParamAware { + + private static final String URL_TEMPLATE_DELIMITER_PREFIX = "{"; + + private static final String URL_TEMPLATE_DELIMITER_SUFFIX = "}"; + + private static final String URL_TYPE_ABSOLUTE = "://"; + + private enum UrlType { + CONTEXT_RELATIVE, RELATIVE, ABSOLUTE + }; + + private static final char[] XML_CHARS = { '&', '<', '>', '"', '\'' }; + + private List params; + + private Set templateParams; + + private UrlType type; + + private String value; + + private String context; + + private String var; + + private int scope = PageContext.PAGE_SCOPE; + + private boolean escapeXml = false; + + // tag lifecycle + + @Override + public int doStartTag() throws JspException { + params = new LinkedList(); + templateParams = new HashSet(); + + return EVAL_BODY_INCLUDE; + } + + @Override + public int doEndTag() throws JspException { + String url = createUrl(); + + if (var == null) { + // print the url to the writer + try { + pageContext.getOut().print(url); + } + catch (IOException e) { + throw new JspException(e); + } + } + else { + // store the url as a variable + pageContext.setAttribute(var, url, scope); + } + + return EVAL_PAGE; + } + + // from ParamAware + + public void addParam(Param param) { + params.add(param); + } + + // support methods + + /** + * Build the URL for the tag from the tag attributes and parameters. + * + * @return the URL value as a String + * @throws JspException + */ + private String createUrl() throws JspException { + HttpServletRequest request = (HttpServletRequest) pageContext + .getRequest(); + HttpServletResponse response = (HttpServletResponse) pageContext + .getResponse(); + StringBuilder url = new StringBuilder(); + + if (type == UrlType.CONTEXT_RELATIVE) { + // add application context to url + if (context == null) { + url.append(request.getContextPath()); + } + else { + url.append(context); + } + } + + if (type != UrlType.RELATIVE && type != UrlType.ABSOLUTE + && !value.startsWith("/")) { + url.append("/"); + } + + url.append(replaceUriTemplateParams(value, params, templateParams)); + url.append(createQueryString(params, templateParams, + (url.indexOf("?") == -1))); + + String urlStr; + if (type != UrlType.ABSOLUTE) { + // add the session identifier if needed + urlStr = response.encodeURL(url.toString()); + } + else { + // do not embed the session identifier in a remote link + urlStr = url.toString(); + } + + if (escapeXml) { + urlStr = escapeXml(urlStr); + } + + return urlStr; + } + + /** + * Builds the query string from available parameters that have not already + * been applied as template params. + * + *

+ * The names and values of parameters are URL encoded. + * + * @param params the parameters to build the query string from + * @param usedParams set of parameter names that have been applied as + * template params + * @param includeQueryStringDelimiter true if the query string should start + * with a '?' instead of '&' + * @return the query string + * @throws JspException + */ + protected String createQueryString(List params, + Set usedParams, boolean includeQueryStringDelimiter) + throws JspException { + StringBuilder qs = new StringBuilder(); + for (Param param : params) { + if (!usedParams.contains(param.getName()) + && param.getName() != null && !"".equals(param.getName())) { + if (includeQueryStringDelimiter && qs.length() == 0) { + qs.append("?"); + } + else { + qs.append("&"); + } + qs.append(urlEncode(param.getName())); + if (param.getValue() != null) { + qs.append("="); + qs.append(urlEncode(param.getValue())); + } + } + } + + return qs.toString(); + } + + /** + * Replaces template markers in the URL matching available parameters. The + * name of matched parameters are added to the used parameters set. + * + *

+ * Parameter values are URL encoded. + * + * @param uri the URL with template parameters to replace + * @param params parameters used to replace template markers + * @param usedParams set of template parameter names that have been replaced + * @return the URL with template parameters replaced + * @throws JspException + */ + protected String replaceUriTemplateParams(String uri, List params, + Set usedParams) throws JspException { + for (Param param : params) { + String template = URL_TEMPLATE_DELIMITER_PREFIX + param.getName() + + URL_TEMPLATE_DELIMITER_SUFFIX; + if (uri.contains(template)) { + usedParams.add(param.getName()); + uri = uri.replace(template, urlEncode(param.getValue())); + } + } + return uri; + } + + /** + * URL encode the provided string using the character encoding for the + * response. + * + * @param value the value to encode + * @return the URL encoded value + * @throws JspException if the character encoding is invalid + */ + protected String urlEncode(String value) throws JspException { + if (value == null) { + return null; + } + + try { + return URLEncoder.encode(value, pageContext.getResponse() + .getCharacterEncoding()); + } + catch (UnsupportedEncodingException e) { + throw new JspException(e); + } + } + + /** + * XML entity encode the provided string. &, <, >, ' and + * " are encoded to their entity values. + * + * @param xml the value to escape + * @return the escaped value + */ + protected String escapeXml(String xml) { + if (xml == null) { + return null; + } + + String escapedXml = xml; + for (char xmlChar : XML_CHARS) { + escapedXml = StringUtils.replace(escapedXml, String + .valueOf(xmlChar), entityValue(xmlChar)); + } + + return escapedXml; + } + + /** + * Convert a character value to a XML entity value. The decimal value of the + * character is used. + * + *

+ * For example, 'A' is converted to "&#65;". + * + * @param xmlChar the character to encode + * @return the entity value + */ + protected String entityValue(char xmlChar) { + return new StringBuilder().append("&#").append( + Integer.toString(xmlChar)).append(";").toString(); + } + + // tag attribute accessors + + /** + * Sets the value of the URL + */ + public void setValue(String value) { + if (value.contains(URL_TYPE_ABSOLUTE)) { + type = UrlType.ABSOLUTE; + this.value = value; + } + else if (value.startsWith("/")) { + type = UrlType.CONTEXT_RELATIVE; + this.value = value; + } + else { + type = UrlType.RELATIVE; + this.value = value; + } + } + + /** + * Sets the context path for the URL. Defaults to the current context + */ + public void setContext(String context) { + if (context.startsWith("/")) { + this.context = context; + } + else { + this.context = "/" + context; + } + } + + /** + * Sets the variable name to expose the URL under. Defaults to rendering the + * URL to the current JspWriter + */ + public void setVar(String var) { + this.var = var; + } + + /** + * Sets the scope to export the URL variable to. This attribute has no + * meaning unless var is also defined. + * + * @param scope the string name of the scope + * @see TagUtils#getScope(String) + */ + public void setScope(String scope) { + this.scope = TagUtils.getScope(scope); + } + + /** + * Instructs the tag to XML entity encode the resulting URL. + *

+ * Defaults to false to maintain compatibility with the JSTL c:url tag. + *

+ * NOTE: Strongly recommended to set as 'true' when rendering + * directly to the JspWriter in an XML or HTML based file. + * + * @param escapeXml string representation of a boolean + * @see Boolean#valueOf(String) + */ + public void setEscapeXml(String escapeXml) { + this.escapeXml = Boolean.valueOf(escapeXml); + } + +} diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/spring.tld b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/spring.tld index 8c8ad97daad..a0bb87c7c8a 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/spring.tld +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/tags/spring.tld @@ -342,4 +342,71 @@ + + url + org.springframework.web.servlet.tags.UrlTag + JSP + URL tag based on the JSTL c:url tag. This variant is fully + backwards compatible with the standard tag. Enhancements include support + for URL template parameters. + + value + true + true + The URL to build. This value can include template place holders + that are replaced with the URL encoded value of the named parameter. Parameters + must be defined using the param tag inside the body of this tag. + + + context + false + true + Specifies a remote application context. The default is the + current application context. + + + var + false + true + The name of the variable to export the URL value to. + + + scope + 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. + + + escapeXml + false + true + Escape XML special characters in the resulting URL. 'true' and + 'false' are supported. Defaults to 'false' to maintain compatibility with + the JSTL c:url tag. Strongly recommended to set as 'true' when rendering + directly to the JspWriter in an XML or HTML based file. + + + + + param + org.springframework.web.servlet.tags.ParamTag + JSP + Parameter tag based on the JSTL c:param tag. The sole purpose is to + support params inside the spring:url tag. + + name + true + true + The name of the parameter. + + + value + false + true + The value of the parameter. + + + diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/ParamTagTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/ParamTagTests.java new file mode 100644 index 00000000000..1066b9596d3 --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/ParamTagTests.java @@ -0,0 +1,109 @@ +/* + * 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 javax.servlet.jsp.JspException; +import javax.servlet.jsp.PageContext; +import javax.servlet.jsp.tagext.Tag; +import javax.servlet.jsp.tagext.TagSupport; + +import org.springframework.mock.web.MockBodyContent; +import org.springframework.mock.web.MockHttpServletResponse; + +/** + * Unit tests for ParamTag + * + * @author Scott Andrews + */ +public class ParamTagTests extends AbstractTagTests { + + private ParamTag tag; + + private MockParamSupportTag parent; + + @Override + protected void setUp() throws Exception { + PageContext context = createPageContext(); + parent = new MockParamSupportTag(); + tag = new ParamTag(); + tag.setPageContext(context); + tag.setParent(parent); + } + + public void testParamWithNameAndValue() throws JspException { + tag.setName("name"); + tag.setValue("value"); + + int action = tag.doEndTag(); + + assertEquals(Tag.EVAL_PAGE, action); + assertEquals("name", parent.getParam().getName()); + assertEquals("value", parent.getParam().getValue()); + } + + public void testParamWithBodyValue() throws JspException { + tag.setName("name"); + tag.setBodyContent(new MockBodyContent("value", + new MockHttpServletResponse())); + + int action = tag.doEndTag(); + + assertEquals(Tag.EVAL_PAGE, action); + assertEquals("name", parent.getParam().getName()); + assertEquals("value", parent.getParam().getValue()); + } + + public void testParamWithNullValue() throws JspException { + tag.setName("name"); + + int action = tag.doEndTag(); + + assertEquals(Tag.EVAL_PAGE, action); + assertEquals("name", parent.getParam().getName()); + assertNull(parent.getParam().getValue()); + } + + public void testParamWithNoParent() { + tag.setName("name"); + tag.setValue("value"); + + tag.setParent(null); + + try { + tag.doEndTag(); + fail("expected JspException"); + } + catch (JspException e) { + // we want this + } + } + + private class MockParamSupportTag extends TagSupport implements ParamAware { + + private Param param; + + public void addParam(Param param) { + this.param = param; + } + + public Param getParam() { + return param; + } + + } + +} diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/ParamTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/ParamTests.java new file mode 100644 index 00000000000..4cd6406a55f --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/ParamTests.java @@ -0,0 +1,49 @@ +/* + * 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 junit.framework.TestCase; + +/** + * Unit tests for Param + * + * @author Scott Andrews + */ +public class ParamTests extends TestCase { + + private Param param; + + @Override + protected void setUp() throws Exception { + param = new Param(); + } + + public void testName() { + param.setName("name"); + assertEquals("name", param.getName()); + } + + public void testValue() { + param.setValue("value"); + assertEquals("value", param.getValue()); + } + + public void testNullDefaults() { + assertNull(param.getName()); + assertNull(param.getValue()); + } +} diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/UrlTagTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/UrlTagTests.java new file mode 100644 index 00000000000..9182a30a50a --- /dev/null +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/tags/UrlTagTests.java @@ -0,0 +1,571 @@ +/* + * 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.lang.reflect.Method; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.PageContext; +import javax.servlet.jsp.tagext.Tag; + +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockPageContext; +import org.springframework.util.ReflectionUtils; + +/** + * Unit tests for UrlTag + * + * @author Scott Andrews + */ +public class UrlTagTests extends AbstractTagTests { + + private UrlTag tag; + + private MockPageContext context; + + @Override + protected void setUp() throws Exception { + context = createPageContext(); + tag = new UrlTag(); + tag.setPageContext(context); + } + + public void testParamSupport() { + assertTrue(tag instanceof ParamAware); + } + + public void testDoStartTag() throws JspException { + int action = tag.doStartTag(); + + assertEquals(Tag.EVAL_BODY_INCLUDE, action); + } + + public void testDoEndTag() throws JspException { + tag.setValue("url/path"); + + tag.doStartTag(); + int action = tag.doEndTag(); + + assertEquals(Tag.EVAL_PAGE, action); + } + + public void testVarDefaultScope() throws JspException { + tag.setValue("url/path"); + tag.setVar("var"); + + tag.doStartTag(); + tag.doEndTag(); + + assertEquals("url/path", context.getAttribute("var", + PageContext.PAGE_SCOPE)); + } + + public void testVarExplicitScope() throws JspException { + tag.setValue("url/path"); + tag.setVar("var"); + tag.setScope("request"); + + tag.doStartTag(); + tag.doEndTag(); + + assertEquals("url/path", context.getAttribute("var", + PageContext.REQUEST_SCOPE)); + } + + public void testSetEscapeXmlDefault() throws JspException { + tag.setValue("url/path"); + tag.setVar("var"); + + tag.doStartTag(); + + Param param = new Param(); + param.setName("n me"); + param.setValue("v&l=e"); + tag.addParam(param); + + param = new Param(); + param.setName("name"); + param.setValue("value2"); + tag.addParam(param); + + tag.doEndTag(); + + assertEquals("url/path?n+me=v%26l%3De&name=value2", context + .getAttribute("var")); + } + + public void testSetEscapeXmlFalse() throws JspException { + tag.setValue("url/path"); + tag.setVar("var"); + tag.setEscapeXml("false"); + + tag.doStartTag(); + + Param param = new Param(); + param.setName("n me"); + param.setValue("v&l=e"); + tag.addParam(param); + + param = new Param(); + param.setName("name"); + param.setValue("value2"); + tag.addParam(param); + + tag.doEndTag(); + + assertEquals("url/path?n+me=v%26l%3De&name=value2", context + .getAttribute("var")); + } + + public void testSetEscapeXmlTrue() throws JspException { + tag.setValue("url/path"); + tag.setVar("var"); + tag.setEscapeXml("true"); + + tag.doStartTag(); + + Param param = new Param(); + param.setName("n me"); + param.setValue("v&l=e"); + tag.addParam(param); + + param = new Param(); + param.setName("name"); + param.setValue("value2"); + tag.addParam(param); + + tag.doEndTag(); + + assertEquals("url/path?n+me=v%26l%3De&name=value2", context + .getAttribute("var")); + } + + public void testCreateQueryStringNoParams() throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + String queryString = tag.createQueryString(params, usedParams, true); + + assertEquals("", queryString); + } + + public void testCreateQueryStringOneParam() throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName("name"); + param.setValue("value"); + params.add(param); + + String queryString = tag.createQueryString(params, usedParams, true); + + assertEquals("?name=value", queryString); + } + + public void testCreateQueryStringOneParamForExsistingQueryString() + throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName("name"); + param.setValue("value"); + params.add(param); + + String queryString = tag.createQueryString(params, usedParams, false); + + assertEquals("&name=value", queryString); + } + + public void testCreateQueryStringOneParamEmptyValue() throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName("name"); + param.setValue(""); + params.add(param); + + String queryString = tag.createQueryString(params, usedParams, true); + + assertEquals("?name=", queryString); + } + + public void testCreateQueryStringOneParamNullValue() throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName("name"); + param.setValue(null); + params.add(param); + + String queryString = tag.createQueryString(params, usedParams, true); + + assertEquals("?name", queryString); + } + + public void testCreateQueryStringOneParamAlreadyUsed() throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName("name"); + param.setValue("value"); + params.add(param); + + usedParams.add("name"); + + String queryString = tag.createQueryString(params, usedParams, true); + + assertEquals("", queryString); + } + + public void testCreateQueryStringTwoParams() throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName("name"); + param.setValue("value"); + params.add(param); + + param = new Param(); + param.setName("name"); + param.setValue("value2"); + params.add(param); + + String queryString = tag.createQueryString(params, usedParams, true); + + assertEquals("?name=value&name=value2", queryString); + } + + public void testCreateQueryStringUrlEncoding() throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName("n me"); + param.setValue("v&l=e"); + params.add(param); + + param = new Param(); + param.setName("name"); + param.setValue("value2"); + params.add(param); + + String queryString = tag.createQueryString(params, usedParams, true); + + assertEquals("?n+me=v%26l%3De&name=value2", queryString); + } + + public void testCreateQueryStringParamNullName() throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName(null); + param.setValue("value"); + params.add(param); + + String queryString = tag.createQueryString(params, usedParams, true); + + assertEquals("", queryString); + } + + public void testCreateQueryStringParamEmptyName() throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName(""); + param.setValue("value"); + params.add(param); + + String queryString = tag.createQueryString(params, usedParams, true); + + assertEquals("", queryString); + } + + public void testReplaceUriTemplateParamsNoParams() throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + String uri = tag.replaceUriTemplateParams("url/path", params, + usedParams); + + assertEquals("url/path", uri); + assertEquals(0, usedParams.size()); + } + + public void testReplaceUriTemplateParamsTemplateWithoutParamMatch() + throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + String uri = tag.replaceUriTemplateParams("url/{path}", params, + usedParams); + + assertEquals("url/{path}", uri); + assertEquals(0, usedParams.size()); + } + + public void testReplaceUriTemplateParamsTemplateWithParamMatch() + throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName("name"); + param.setValue("value"); + params.add(param); + + String uri = tag.replaceUriTemplateParams("url/{name}", params, + usedParams); + + assertEquals("url/value", uri); + assertEquals(1, usedParams.size()); + assertTrue(usedParams.contains("name")); + } + + public void testReplaceUriTemplateParamsTemplateWithParamMatchNamePreEncoding() + throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName("n me"); + param.setValue("value"); + params.add(param); + + String uri = tag.replaceUriTemplateParams("url/{n me}", params, + usedParams); + + assertEquals("url/value", uri); + assertEquals(1, usedParams.size()); + assertTrue(usedParams.contains("n me")); + } + + public void testReplaceUriTemplateParamsTemplateWithParamMatchValueEncoded() + throws JspException { + List params = new LinkedList(); + Set usedParams = new HashSet(); + + Param param = new Param(); + param.setName("name"); + param.setValue("v lue"); + params.add(param); + + String uri = tag.replaceUriTemplateParams("url/{name}", params, + usedParams); + + assertEquals("url/v+lue", uri); + assertEquals(1, usedParams.size()); + assertTrue(usedParams.contains("name")); + } + + public void testCreateUrlRemoteServer() throws JspException { + tag.setValue("http://www.springframework.org/"); + + tag.doStartTag(); + + // String uri = tag.createUrl(); + String uri = invokeCreateUrl(tag); + + assertEquals("http://www.springframework.org/", uri); + } + + public void testCreateUrlRelative() throws JspException { + tag.setValue("url/path"); + + tag.doStartTag(); + + String uri = invokeCreateUrl(tag); + + assertEquals("url/path", uri); + } + + public void testCreateUrlLocalContext() throws JspException { + ((MockHttpServletRequest) context.getRequest()) + .setContextPath("/app-context"); + + tag.setValue("/url/path"); + + tag.doStartTag(); + + String uri = invokeCreateUrl(tag); + + assertEquals("/app-context/url/path", uri); + } + + public void testCreateUrlRemoteContext() throws JspException { + ((MockHttpServletRequest) context.getRequest()) + .setContextPath("/app-context"); + + tag.setValue("/url/path"); + tag.setContext("some-other-context"); + + tag.doStartTag(); + + String uri = invokeCreateUrl(tag); + + assertEquals("/some-other-context/url/path", uri); + } + + public void testCreateUrlRemoteContextWithSlash() throws JspException { + ((MockHttpServletRequest) context.getRequest()) + .setContextPath("/app-context"); + + tag.setValue("/url/path"); + tag.setContext("/some-other-context"); + + tag.doStartTag(); + + String uri = invokeCreateUrl(tag); + + assertEquals("/some-other-context/url/path", uri); + } + + public void testCreateUrlWithParams() throws JspException { + tag.setValue("url/path"); + + tag.doStartTag(); + + Param param = new Param(); + param.setName("name"); + param.setValue("value"); + tag.addParam(param); + + param = new Param(); + param.setName("n me"); + param.setValue("v lue"); + tag.addParam(param); + + String uri = invokeCreateUrl(tag); + + assertEquals("url/path?name=value&n+me=v+lue", uri); + } + + public void testCreateUrlWithTemplateParams() throws JspException { + tag.setValue("url/{name}"); + + tag.doStartTag(); + + Param param = new Param(); + param.setName("name"); + param.setValue("value"); + tag.addParam(param); + + param = new Param(); + param.setName("n me"); + param.setValue("v lue"); + tag.addParam(param); + + String uri = invokeCreateUrl(tag); + + assertEquals("url/value?n+me=v+lue", uri); + } + + public void testCreateUrlWithParamAndExsistingQueryString() + throws JspException { + tag.setValue("url/path?foo=bar"); + + tag.doStartTag(); + + Param param = new Param(); + param.setName("name"); + param.setValue("value"); + tag.addParam(param); + + String uri = invokeCreateUrl(tag); + + assertEquals("url/path?foo=bar&name=value", uri); + } + + public void testUrlEncode() throws JspException { + assertEquals("my+name", tag.urlEncode("my name")); + } + + public void testUrlEncodeNull() throws JspException { + assertNull(tag.urlEncode(null)); + } + + public void testUrlEncodeBadEncoding() { + context.getResponse().setCharacterEncoding("bad encoding"); + + try { + tag.urlEncode("my name"); + fail("expected JspException"); + } + catch (JspException e) { + // we want this + } + } + + public void testEscapeXml() { + assertEquals("<script type="text/javascript">", tag + .escapeXml("