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
master
Sam Brannen 9 years ago
parent f2f58f1677
commit 89e504c2f1
  1. 33
      spring-core/src/main/java/org/springframework/util/MimeType.java
  2. 40
      spring-core/src/test/java/org/springframework/util/MimeTypeTests.java
  3. 2
      spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.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<MimeType>, 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<String, String> 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

@ -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);
}
}

@ -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"));

Loading…
Cancel
Save