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 index 6a10eed6f9a..f6350a9cfff 100644 --- 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 @@ -33,6 +33,7 @@ import org.springframework.web.util.ExpressionEvaluationUtils; import org.springframework.web.util.HtmlUtils; import org.springframework.web.util.JavaScriptUtils; import org.springframework.web.util.TagUtils; +import org.springframework.web.util.UriUtils; /** * JSP tag for creating URLs. Modeled after the JSTL c:url tag with backwards @@ -296,41 +297,13 @@ public class UrlTag extends HtmlEscapingAwareTag implements ParamAware { } try { String encoding = pageContext.getResponse().getCharacterEncoding(); - String formUrlEncodedValue = URLEncoder.encode(value, encoding); - if (!formUrlEncodedValue.contains("+")) { - return formUrlEncodedValue; - } - String spaceEncoding = this.urlEncode(' ', encoding); - return formUrlEncodedValue.replace("+", spaceEncoding); + return UriUtils.encode(value, encoding); } catch (UnsupportedEncodingException ex) { throw new JspException(ex); } } - /* - * based on URLCodec from Apache Commons Codec - */ - protected String urlEncode(Character c, String enc) throws UnsupportedEncodingException { - if (c == null) { - return null; - } - byte[] bytes = c.toString().getBytes(enc); - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < bytes.length; i++) { - int b = bytes[i]; - if (b < 0) { - b = 256 + b; - } - char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16)); - char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16)); - builder.append('%'); - builder.append(hex1); - builder.append(hex2); - } - return builder.toString(); - } - /** * Internal enum that classifies URLs by type. */ diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/handler/PathMatchingUrlHandlerMappingTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/handler/PathMatchingUrlHandlerMappingTests.java index c0add0066d3..3b86164300d 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/handler/PathMatchingUrlHandlerMappingTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/handler/PathMatchingUrlHandlerMappingTests.java @@ -26,11 +26,15 @@ import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerMapping; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + /** * @author Alef Arendsen * @author Juergen Hoeller */ -public class PathMatchingUrlHandlerMappingTests extends TestCase { +public class PathMatchingUrlHandlerMappingTests { public static final String CONF = "/org/springframework/web/servlet/handler/map3.xml"; @@ -38,6 +42,7 @@ public class PathMatchingUrlHandlerMappingTests extends TestCase { private ConfigurableWebApplicationContext wac; + @Before public void setUp() throws Exception { MockServletContext sc = new MockServletContext(""); wac = new XmlWebApplicationContext(); @@ -47,7 +52,8 @@ public class PathMatchingUrlHandlerMappingTests extends TestCase { hm = (HandlerMapping) wac.getBean("urlMapping"); } - public void testRequestsWithHandlers() throws Exception { + @Test + public void requestsWithHandlers() throws Exception { Object bean = wac.getBean("mainController"); MockHttpServletRequest req = new MockHttpServletRequest("GET", "/welcome.html"); @@ -63,7 +69,8 @@ public class PathMatchingUrlHandlerMappingTests extends TestCase { assertTrue("Handler is correct bean", hec != null && hec.getHandler() == bean); } - public void testActualPathMatching() throws Exception { + @Test + public void actualPathMatching() throws Exception { // there a couple of mappings defined with which we can test the // path matching, let's do that... @@ -222,14 +229,16 @@ public class PathMatchingUrlHandlerMappingTests extends TestCase { assertTrue("Handler is correct bean", hec != null && hec.getHandler() == defaultBean); } - public void testDefaultMapping() throws Exception { + @Test + public void defaultMapping() throws Exception { Object bean = wac.getBean("starController"); MockHttpServletRequest req = new MockHttpServletRequest("GET", "/goggog.html"); HandlerExecutionChain hec = getHandler(req); assertTrue("Handler is correct bean", hec != null && hec.getHandler() == bean); } - public void testMappingExposedInRequest() throws Exception { + @Test + public void mappingExposedInRequest() throws Exception { Object bean = wac.getBean("mainController"); MockHttpServletRequest req = new MockHttpServletRequest("GET", "/show.html"); HandlerExecutionChain hec = getHandler(req); @@ -241,8 +250,8 @@ public class PathMatchingUrlHandlerMappingTests extends TestCase { HandlerExecutionChain hec = hm.getHandler(req); HandlerInterceptor[] interceptors = hec.getInterceptors(); if (interceptors != null) { - for (int i = 0; i < interceptors.length; i++) { - interceptors[i].preHandle(req, null, hec.getHandler()); + for (HandlerInterceptor interceptor : interceptors) { + interceptor.preHandle(req, null, hec.getHandler()); } } return hec; 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 index e60ea953a44..9ba86bb6131 100644 --- 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 @@ -554,13 +554,6 @@ public class UrlTagTests extends AbstractTagTests { assertEquals("my%20name%2Bis", tag.urlEncode("my name+is")); } - public void testUrlEncode_character() throws UnsupportedEncodingException { - assertEquals("%20", tag.urlEncode(' ', "UTF-8")); - assertEquals(" ", URLDecoder.decode("%20", "UTF-8")); - assertEquals("%FE%FF%00%20", tag.urlEncode(' ', "UTF-16")); - assertEquals("%40", tag.urlEncode(' ', "IBM-Thai")); - } - public void testUrlEncodeNull() throws JspException { assertNull(tag.urlEncode(null)); } diff --git a/org.springframework.web/src/main/java/org/springframework/web/util/UriUtils.java b/org.springframework.web/src/main/java/org/springframework/web/util/UriUtils.java new file mode 100644 index 00000000000..c7e837b06d8 --- /dev/null +++ b/org.springframework.web/src/main/java/org/springframework/web/util/UriUtils.java @@ -0,0 +1,147 @@ +/* + * Copyright 2002-2009 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.util; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.BitSet; + +import org.springframework.util.Assert; + +/** + * Utility class for URI encoding. Based on RFC 2396. + * + *
Effectively, the encoding and decoding methods in this class + * are similar to those found in {@link java.net.URLEncoder} and + * {@link java.net.URLDecoder}, except that the space character + * is encoded as {@code %20}, not {@code +}. + * + * @author Arjen Poutsma + * @since 3.0 + * @see RFC 2396 + */ +public abstract class UriUtils { + + private static final BitSet notEncoded = new BitSet(256); + + static { + for (int i = 'a'; i <= 'z'; i++) { + notEncoded.set(i); + } + for (int i = 'A'; i <= 'Z'; i++) { + notEncoded.set(i); + } + for (int i = '0'; i <= '9'; i++) { + notEncoded.set(i); + } + notEncoded.set('-'); + notEncoded.set('_'); + notEncoded.set('.'); + notEncoded.set('*'); + + } + + /** + * Encodes the given source URI into an encoded String. Based on the following + * rules: + *
%xy" format.
+ * %xy" is interpreted as a hexadecimal
+ * representation of the character.
+ *