diff --git a/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java b/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java index 7a5a033d7bc..968942cd084 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java +++ b/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java @@ -274,7 +274,7 @@ public abstract class MimeTypeUtils { if (!StringUtils.hasLength(mimeTypes)) { return Collections.emptyList(); } - String[] tokens = mimeTypes.split(",\\s*"); + String[] tokens = StringUtils.tokenizeToStringArray(mimeTypes, ","); List result = new ArrayList<>(tokens.length); for (String token : tokens) { result.add(parseMimeType(token)); diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java index 444dbdfdba2..07949f4d95c 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java @@ -229,10 +229,10 @@ public class StompHeaderAccessor extends SimpMessageHeaderAccessor { public long[] getHeartbeat() { String rawValue = getFirstNativeHeader(STOMP_HEARTBEAT_HEADER); - if (!StringUtils.hasText(rawValue)) { + String[] rawValues = StringUtils.split(rawValue, ","); + if (rawValues == null) { return Arrays.copyOf(DEFAULT_HEARTBEAT, 2); } - String[] rawValues = StringUtils.commaDelimitedListToStringArray(rawValue); return new long[] {Long.valueOf(rawValues[0]), Long.valueOf(rawValues[1])}; } @@ -298,7 +298,7 @@ public class StompHeaderAccessor extends SimpMessageHeaderAccessor { } public void setHeartbeat(long cx, long cy) { - setNativeHeader(STOMP_HEARTBEAT_HEADER, StringUtils.arrayToCommaDelimitedString(new Object[]{cx, cy})); + setNativeHeader(STOMP_HEARTBEAT_HEADER, cx + "," + cy); } public void setAck(String ack) { diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaders.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaders.java index e917a922b2a..5530aae6c08 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaders.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaders.java @@ -223,9 +223,14 @@ public class StompHeaders implements MultiValueMap, Serializable * Applies to the CONNECT and CONNECTED frames. */ public void setHeartbeat(long[] heartbeat) { - Assert.notNull(heartbeat); + if (heartbeat == null || heartbeat.length != 2) { + throw new IllegalArgumentException("Heart-beat array must be of length 2, not " + + (heartbeat != null ? heartbeat.length : "null")); + } String value = heartbeat[0] + "," + heartbeat[1]; - Assert.isTrue(heartbeat[0] >= 0 && heartbeat[1] >= 0, "Heart-beat values cannot be negative: " + value); + if (heartbeat[0] < 0 || heartbeat[1] < 0) { + throw new IllegalArgumentException("Heart-beat values cannot be negative: " + value); + } set(HEARTBEAT, value); } @@ -234,10 +239,10 @@ public class StompHeaders implements MultiValueMap, Serializable */ public long[] getHeartbeat() { String rawValue = getFirst(HEARTBEAT); - if (!StringUtils.hasText(rawValue)) { + String[] rawValues = StringUtils.split(rawValue, ","); + if (rawValues == null) { return null; } - String[] rawValues = StringUtils.commaDelimitedListToStringArray(rawValue); return new long[] {Long.valueOf(rawValues[0]), Long.valueOf(rawValues[1])}; } @@ -497,14 +502,8 @@ public class StompHeaders implements MultiValueMap, Serializable @Override public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!(other instanceof StompHeaders)) { - return false; - } - StompHeaders otherHeaders = (StompHeaders) other; - return this.headers.equals(otherHeaders.headers); + return (this == other || (other instanceof StompHeaders && + this.headers.equals(((StompHeaders) other).headers))); } @Override diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java index a8c1c87be7b..81391ebb1be 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java @@ -50,6 +50,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; @@ -213,8 +214,7 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable { private void authType(MockHttpServletRequest request) { String authorization = header("Authorization"); if (authorization != null) { - String[] authzParts = authorization.split(": "); - request.setAuthType(authzParts[0]); + request.setAuthType(StringUtils.split(authorization, ": ")[0]); } } @@ -349,9 +349,9 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable { request.addPreferredLocale(Locale.getDefault()); } else { - String[] locales = locale.split(", "); - for (int i = locales.length - 1; i >= 0; i--) { - request.addPreferredLocale(parseLocale(locales[i])); + String[] tokens = StringUtils.tokenizeToStringArray(locale, ","); + for (int i = tokens.length - 1; i >= 0; i--) { + request.addPreferredLocale(parseLocale(tokens[i])); } } } diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index 61c365f9630..d3b4a04e66a 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -476,7 +476,7 @@ public class HttpHeaders implements MultiValueMap, Serializable List result = new ArrayList<>(); String value = getFirst(ACCESS_CONTROL_ALLOW_METHODS); if (value != null) { - String[] tokens = StringUtils.tokenizeToStringArray(value, ",", true, true); + String[] tokens = StringUtils.tokenizeToStringArray(value, ","); for (String token : tokens) { HttpMethod resolved = HttpMethod.resolve(token); if (resolved != null) { @@ -580,10 +580,10 @@ public class HttpHeaders implements MultiValueMap, Serializable * as specified by the {@code Accept-Charset} header. */ public List getAcceptCharset() { - List result = new ArrayList<>(); String value = getFirst(ACCEPT_CHARSET); if (value != null) { - String[] tokens = value.split(",\\s*"); + String[] tokens = StringUtils.tokenizeToStringArray(value, ","); + List result = new ArrayList<>(tokens.length); for (String token : tokens) { int paramIdx = token.indexOf(';'); String charsetName; @@ -597,8 +597,11 @@ public class HttpHeaders implements MultiValueMap, Serializable result.add(Charset.forName(charsetName)); } } + return result; + } + else { + return Collections.emptyList(); } - return result; } /** @@ -617,8 +620,8 @@ public class HttpHeaders implements MultiValueMap, Serializable public Set getAllow() { String value = getFirst(ALLOW); if (!StringUtils.isEmpty(value)) { - List result = new LinkedList<>(); - String[] tokens = value.split(",\\s*"); + String[] tokens = StringUtils.tokenizeToStringArray(value, ","); + List result = new ArrayList<>(tokens.length); for (String token : tokens) { HttpMethod resolved = HttpMethod.resolve(token); if (resolved != null) { diff --git a/spring-web/src/main/java/org/springframework/http/HttpRange.java b/spring-web/src/main/java/org/springframework/http/HttpRange.java index 89cad5d9c58..421c8bd7779 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpRange.java +++ b/spring-web/src/main/java/org/springframework/http/HttpRange.java @@ -132,7 +132,7 @@ public abstract class HttpRange { } ranges = ranges.substring(BYTE_RANGE_PREFIX.length()); - String[] tokens = ranges.split(",\\s*"); + String[] tokens = StringUtils.tokenizeToStringArray(ranges, ","); List result = new ArrayList<>(tokens.length); for (String token : tokens) { result.add(parseRange(token)); diff --git a/spring-web/src/main/java/org/springframework/http/MediaType.java b/spring-web/src/main/java/org/springframework/http/MediaType.java index f7bee293061..ef81ac5ed0b 100644 --- a/spring-web/src/main/java/org/springframework/http/MediaType.java +++ b/spring-web/src/main/java/org/springframework/http/MediaType.java @@ -439,7 +439,7 @@ public class MediaType extends MimeType implements Serializable { if (!StringUtils.hasLength(mediaTypes)) { return Collections.emptyList(); } - String[] tokens = mediaTypes.split(",\\s*"); + String[] tokens = StringUtils.tokenizeToStringArray(mediaTypes, ","); List result = new ArrayList<>(tokens.length); for (String token : tokens) { result.add(parseMediaType(token)); diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java index a25b59634ac..36392537615 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java @@ -673,7 +673,7 @@ public class UriComponentsBuilder implements Cloneable { UriComponentsBuilder adaptFromForwardedHeaders(HttpHeaders headers) { String forwardedHeader = headers.getFirst("Forwarded"); if (StringUtils.hasText(forwardedHeader)) { - String forwardedToUse = StringUtils.commaDelimitedListToStringArray(forwardedHeader)[0]; + String forwardedToUse = StringUtils.tokenizeToStringArray(forwardedHeader, ",")[0]; Matcher matcher = FORWARDED_HOST_PATTERN.matcher(forwardedToUse); if (matcher.find()) { host(matcher.group(1).trim()); @@ -686,10 +686,9 @@ public class UriComponentsBuilder implements Cloneable { else { String hostHeader = headers.getFirst("X-Forwarded-Host"); if (StringUtils.hasText(hostHeader)) { - String[] hosts = StringUtils.commaDelimitedListToStringArray(hostHeader); - String hostToUse = hosts[0]; - if (hostToUse.contains(":")) { - String[] hostAndPort = StringUtils.split(hostToUse, ":"); + String hostToUse = StringUtils.tokenizeToStringArray(hostHeader, ",")[0]; + String[] hostAndPort = StringUtils.split(hostToUse, ":"); + if (hostAndPort != null) { host(hostAndPort[0]); port(Integer.parseInt(hostAndPort[1])); } @@ -701,14 +700,12 @@ public class UriComponentsBuilder implements Cloneable { String portHeader = headers.getFirst("X-Forwarded-Port"); if (StringUtils.hasText(portHeader)) { - String[] ports = StringUtils.commaDelimitedListToStringArray(portHeader); - port(Integer.parseInt(ports[0])); + port(Integer.parseInt(StringUtils.tokenizeToStringArray(portHeader, ",")[0])); } String protocolHeader = headers.getFirst("X-Forwarded-Proto"); if (StringUtils.hasText(protocolHeader)) { - String[] protocols = StringUtils.commaDelimitedListToStringArray(protocolHeader); - scheme(protocols[0]); + scheme(StringUtils.tokenizeToStringArray(protocolHeader, ",")[0]); } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java index 89fea5fc281..8cf85626612 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java @@ -44,6 +44,7 @@ import org.springframework.util.StringUtils; * e.g. extensions "foo, bar" will be executed as "bar(foo(message))".

* * @author Brian Clozel + * @author Juergen Hoeller * @since 4.0 * @see WebSocket Protocol Extensions, RFC 6455 - Section 9 */ @@ -68,46 +69,79 @@ public class WebSocketExtension { * @param parameters the parameters */ public WebSocketExtension(String name, Map parameters) { - Assert.hasLength(name, "extension name must not be empty"); + Assert.hasLength(name, "Extension name must not be empty"); this.name = name; if (!CollectionUtils.isEmpty(parameters)) { - Map m = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ENGLISH); - m.putAll(parameters); - this.parameters = Collections.unmodifiableMap(m); + Map map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ENGLISH); + map.putAll(parameters); + this.parameters = Collections.unmodifiableMap(map); } else { this.parameters = Collections.emptyMap(); } } + /** - * @return the name of the extension + * Return the name of the extension (never {@code null) or empty}. */ public String getName() { return this.name; } /** - * @return the parameters of the extension, never {@code null} + * Return the parameters of the extension (never {@code null}). */ public Map getParameters() { return this.parameters; } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + WebSocketExtension otherExt = (WebSocketExtension) other; + return (this.name.equals(otherExt.name) && this.parameters.equals(otherExt.parameters)); + } + + @Override + public int hashCode() { + return this.name.hashCode() * 31 + this.parameters.hashCode(); + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append(this.name); + for (String param : parameters.keySet()) { + str.append(';'); + str.append(param); + str.append('='); + str.append(this.parameters.get(param)); + } + return str.toString(); + } + + /** * Parse the given, comma-separated string into a list of {@code WebSocketExtension} objects. - *

This method can be used to parse a "Sec-WebSocket-Extension" extensions. + *

This method can be used to parse a "Sec-WebSocket-Extension" header. * @param extensions the string to parse * @return the list of extensions * @throws IllegalArgumentException if the string cannot be parsed */ public static List parseExtensions(String extensions) { - if (extensions == null || !StringUtils.hasText(extensions)) { + if (!StringUtils.hasText(extensions)) { return Collections.emptyList(); } else { List result = new ArrayList<>(); - for (String token : extensions.split(",")) { + for (String token : StringUtils.tokenizeToStringArray(extensions, ",")) { result.add(parseExtension(token)); } return result; @@ -115,7 +149,9 @@ public class WebSocketExtension { } private static WebSocketExtension parseExtension(String extension) { - Assert.doesNotContain(extension, ",", "Expected a single extension value: " + extension); + if (extension.contains(",")) { + throw new IllegalArgumentException("Expected single extension value: [" + extension + "]"); + } String[] parts = StringUtils.tokenizeToStringArray(extension, ";"); String name = parts[0].trim(); @@ -136,42 +172,4 @@ public class WebSocketExtension { return new WebSocketExtension(name, parameters); } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if ((o == null) || (getClass() != o.getClass())) { - return false; - } - WebSocketExtension that = (WebSocketExtension) o; - if (!name.equals(that.name)) { - return false; - } - if (!parameters.equals(that.parameters)) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int result = name.hashCode(); - result = 31 * result + parameters.hashCode(); - return result; - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - str.append(this.name); - for (String param : parameters.keySet()) { - str.append(';'); - str.append(param); - str.append('='); - str.append(this.parameters.get(param)); - } - return str.toString(); - } - } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java index a3802662fa5..23cd292126c 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java @@ -110,7 +110,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda @Override public String[] getSupportedVersions() { - return StringUtils.commaDelimitedListToStringArray(Version.getSupportedWireProtocolVersions()); + return StringUtils.tokenizeToStringArray(Version.getSupportedWireProtocolVersions(), ","); } protected List getInstalledExtensions(WebSocketContainer container) {