From 89e504c2f1f5a72ccce054dbd616755aa9678fff Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 23 Jun 2015 22:30:25 +0200 Subject: [PATCH] Uses Charset instead of String in MimeType.equals() Prior to this commit, Spring's MimeType checked for equality between two MIME types based on the equality of their properties maps; however, the properties maps contain string representations of the "charset" values. Thus, "UTF-8" is never equal to "utf-8" which breaks the contract for character set names which must be compared in a case-insensitive manner. This commit addresses this issue by ensuring that "charset" properties in MimeType instances are compared as Java Charset instances, thereby ignoring case when checking for equality between charset names. Issue: SPR-13157 --- .../org/springframework/util/MimeType.java | 33 ++++++++++++++- .../springframework/util/MimeTypeTests.java | 40 +++++++++++++------ .../client/AbstractJettyServerTestCase.java | 2 +- 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/MimeType.java b/spring-core/src/main/java/org/springframework/util/MimeType.java index f4ca7953ed..ccfd56ada9 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeType.java +++ b/spring-core/src/main/java/org/springframework/util/MimeType.java @@ -25,6 +25,7 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Map.Entry; import java.util.TreeSet; /** @@ -430,7 +431,37 @@ public class MimeType implements Comparable, Serializable { MimeType otherType = (MimeType) other; return (this.type.equalsIgnoreCase(otherType.type) && this.subtype.equalsIgnoreCase(otherType.subtype) && - this.parameters.equals(otherType.parameters)); + parametersAreEqual(otherType)); + } + + /** + * Determine if the parameters in this {@code MimeType} and the supplied + * {@code MimeType} are equal, performing case-insensitive comparisons + * for {@link Charset}s. + * @since 4.2 + */ + private boolean parametersAreEqual(MimeType that) { + if (this.parameters.size() != that.parameters.size()) { + return false; + } + + for (Entry entry : this.parameters.entrySet()) { + String key = entry.getKey(); + if (!that.parameters.containsKey(key)) { + return false; + } + + if (PARAM_CHARSET.equals(key)) { + if (!ObjectUtils.nullSafeEquals(this.getCharSet(), that.getCharSet())) { + return false; + } + } + else if (!ObjectUtils.nullSafeEquals(this.parameters.get(key), that.parameters.get(key))) { + return false; + } + } + + return true; } @Override diff --git a/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java b/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java index da7d9c1178..b9d3480fd7 100644 --- a/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java +++ b/spring-core/src/test/java/org/springframework/util/MimeTypeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 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. @@ -27,15 +27,18 @@ import org.junit.Test; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; +import static java.util.Collections.singletonMap; import static org.junit.Assert.*; /** + * Unit tests for {@link MimeType}. + * * @author Arjen Poutsma * @author Juergen Hoeller + * @author Sam Brannen */ public class MimeTypeTests { - @Test(expected = IllegalArgumentException.class) public void slashInSubtype() { new MimeType("text", "/"); @@ -85,7 +88,7 @@ public class MimeTypeTests { } @Test - public void testWithConversionService() { + public void withConversionService() { ConversionService conversionService = new DefaultConversionService(); assertTrue(conversionService.canConvert(String.class, MimeType.class)); MimeType mimeType = MimeType.valueOf("application/xml"); @@ -211,16 +214,18 @@ public class MimeTypeTests { MimeTypeUtils.parseMimeType("text/html; charset=foo-bar"); } - // SPR-8917 - + /** + * SPR-8917 + */ @Test public void parseMimeTypeQuotedParameterValue() { MimeType mimeType = MimeTypeUtils.parseMimeType("audio/*;attr=\"v>alue\""); assertEquals("\"v>alue\"", mimeType.getParameter("attr")); } - // SPR-8917 - + /** + * SPR-8917 + */ @Test public void parseMimeTypeSingleQuotedParameterValue() { MimeType mimeType = MimeTypeUtils.parseMimeType("audio/*;attr='v>alue'"); @@ -249,7 +254,7 @@ public class MimeTypeTests { MimeType audioBasic = new MimeType("audio", "basic"); MimeType audio = new MimeType("audio"); MimeType audioWave = new MimeType("audio", "wave"); - MimeType audioBasicLevel = new MimeType("audio", "basic", Collections.singletonMap("level", "1")); + MimeType audioBasicLevel = new MimeType("audio", "basic", singletonMap("level", "1")); // equal assertEquals("Invalid comparison result", 0, audioBasic.compareTo(audioBasic)); @@ -284,16 +289,27 @@ public class MimeTypeTests { assertEquals("Invalid comparison result", 0, m1.compareTo(m2)); assertEquals("Invalid comparison result", 0, m2.compareTo(m1)); - m1 = new MimeType("audio", "basic", Collections.singletonMap("foo", "bar")); - m2 = new MimeType("audio", "basic", Collections.singletonMap("Foo", "bar")); + m1 = new MimeType("audio", "basic", singletonMap("foo", "bar")); + m2 = new MimeType("audio", "basic", singletonMap("Foo", "bar")); assertEquals("Invalid comparison result", 0, m1.compareTo(m2)); assertEquals("Invalid comparison result", 0, m2.compareTo(m1)); - m1 = new MimeType("audio", "basic", Collections.singletonMap("foo", "bar")); - m2 = new MimeType("audio", "basic", Collections.singletonMap("foo", "Bar")); + m1 = new MimeType("audio", "basic", singletonMap("foo", "bar")); + m2 = new MimeType("audio", "basic", singletonMap("foo", "Bar")); assertTrue("Invalid comparison result", m1.compareTo(m2) != 0); assertTrue("Invalid comparison result", m2.compareTo(m1) != 0); } + /** + * SPR-13157 + * @since 4.2 + */ + @Test + public void equalsIsCaseInsensitiveForCharsets() { + MimeType m1 = new MimeType("text", "plain", singletonMap("charset", "UTF-8")); + MimeType m2 = new MimeType("text", "plain", singletonMap("charset", "utf-8")); + assertEquals(m1, m2); + assertEquals(m2, m1); + } } diff --git a/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java b/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java index f633dec43e..5f54060ba9 100644 --- a/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java +++ b/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java @@ -56,7 +56,7 @@ public class AbstractJettyServerTestCase { protected static final String baseUrl = "http://localhost:" + port; - protected static final MediaType textContentType = new MediaType("text", "plain", Collections.singletonMap("charset", "utf-8")); + protected static final MediaType textContentType = new MediaType("text", "plain", Collections.singletonMap("charset", "UTF-8")); protected static final MediaType jsonContentType = new MediaType("application", "json", Collections.singletonMap("charset", "utf-8"));