Browse Source

Optimize MediaType(MediaType, Charset) constructor

Prior to this commit, the `MediaType` and `MimeType` "copy" constructors
would not leverage the fact that the existing instance has been
validated already (types, subtype and parameters have been checked
already for errors) and the entire validation would be performed again.
This would also allocate map instances in the process.

This commit ensures that the already validated information is reused
directly and that we avoid unnessecary operations and allocations for
such constructors.

Closes gh-36318
pull/36325/head
Brian Clozel 1 month ago
parent
commit
97e96895db
  1. 81
      spring-core/src/main/java/org/springframework/util/MimeType.java
  2. 2
      spring-web/src/main/java/org/springframework/http/MediaType.java

81
spring-core/src/main/java/org/springframework/util/MimeType.java

@ -140,7 +140,26 @@ public class MimeType implements Comparable<MimeType>, Serializable { @@ -140,7 +140,26 @@ public class MimeType implements Comparable<MimeType>, Serializable {
*/
public MimeType(String type, String subtype, Charset charset) {
this(type, subtype, Collections.singletonMap(PARAM_CHARSET, charset.name()));
this.resolvedCharset = charset;
}
/**
* Create a new {@code MimeType} for the given type, subtype, and parameters.
* @param type the primary type
* @param subtype the subtype
* @param parameters the parameters (may be {@code null})
* @throws IllegalArgumentException if any of the parameters contains illegal characters
*/
public MimeType(String type, String subtype, @Nullable Map<String, String> parameters) {
Assert.hasLength(type, "'type' must not be empty");
Assert.hasLength(subtype, "'subtype' must not be empty");
checkToken(type);
checkToken(subtype);
this.type = type.toLowerCase(Locale.ROOT);
this.subtype = subtype.toLowerCase(Locale.ROOT);
this.parameters = createParametersMap(parameters);
if (this.parameters.containsKey(PARAM_CHARSET)) {
this.resolvedCharset = Charset.forName(unquote(this.parameters.get(PARAM_CHARSET)));
}
}
/**
@ -152,7 +171,12 @@ public class MimeType implements Comparable<MimeType>, Serializable { @@ -152,7 +171,12 @@ public class MimeType implements Comparable<MimeType>, Serializable {
* @since 4.3
*/
public MimeType(MimeType other, Charset charset) {
this(other.getType(), other.getSubtype(), addCharsetParameter(charset, other.getParameters()));
this.type = other.type;
this.subtype = other.subtype;
Map<String, String> map = new LinkedCaseInsensitiveMap<>(other.parameters.size() + 1, Locale.ROOT);
map.putAll(other.parameters);
map.put(PARAM_CHARSET, charset.name());
this.parameters = Collections.unmodifiableMap(map);
this.resolvedCharset = charset;
}
@ -164,33 +188,11 @@ public class MimeType implements Comparable<MimeType>, Serializable { @@ -164,33 +188,11 @@ public class MimeType implements Comparable<MimeType>, Serializable {
* @throws IllegalArgumentException if any of the parameters contains illegal characters
*/
public MimeType(MimeType other, @Nullable Map<String, String> parameters) {
this(other.getType(), other.getSubtype(), parameters);
}
/**
* Create a new {@code MimeType} for the given type, subtype, and parameters.
* @param type the primary type
* @param subtype the subtype
* @param parameters the parameters (may be {@code null})
* @throws IllegalArgumentException if any of the parameters contains illegal characters
*/
public MimeType(String type, String subtype, @Nullable Map<String, String> parameters) {
Assert.hasLength(type, "'type' must not be empty");
Assert.hasLength(subtype, "'subtype' must not be empty");
checkToken(type);
checkToken(subtype);
this.type = type.toLowerCase(Locale.ROOT);
this.subtype = subtype.toLowerCase(Locale.ROOT);
if (!CollectionUtils.isEmpty(parameters)) {
Map<String, String> map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ROOT);
parameters.forEach((parameter, value) -> {
checkParameters(parameter, value);
map.put(parameter, value);
});
this.parameters = Collections.unmodifiableMap(map);
}
else {
this.parameters = Collections.emptyMap();
this.type = other.type;
this.subtype = other.subtype;
this.parameters = createParametersMap(parameters);
if (this.parameters.containsKey(PARAM_CHARSET)) {
this.resolvedCharset = Charset.forName(unquote(this.parameters.get(PARAM_CHARSET)));
}
}
@ -223,16 +225,25 @@ public class MimeType implements Comparable<MimeType>, Serializable { @@ -223,16 +225,25 @@ public class MimeType implements Comparable<MimeType>, Serializable {
}
}
private Map<String, String> createParametersMap(@Nullable Map<String, String> parameters) {
if (!CollectionUtils.isEmpty(parameters)) {
Map<String, String> map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ROOT);
parameters.forEach((parameter, value) -> {
checkParameters(parameter, value);
map.put(parameter, value);
});
return Collections.unmodifiableMap(map);
}
else {
return Collections.emptyMap();
}
}
protected void checkParameters(String parameter, String value) {
Assert.hasLength(parameter, "'parameter' must not be empty");
Assert.hasLength(value, "'value' must not be empty");
checkToken(parameter);
if (PARAM_CHARSET.equals(parameter)) {
if (this.resolvedCharset == null) {
this.resolvedCharset = Charset.forName(unquote(value));
}
}
else if (!isQuotedString(value)) {
if (!isQuotedString(value)) {
checkToken(value);
}
}

2
spring-web/src/main/java/org/springframework/http/MediaType.java

@ -458,7 +458,7 @@ public class MediaType extends MimeType implements Serializable { @@ -458,7 +458,7 @@ public class MediaType extends MimeType implements Serializable {
* @throws IllegalArgumentException if any of the parameters contain illegal characters
*/
public MediaType(MediaType other, @Nullable Map<String, String> parameters) {
super(other.getType(), other.getSubtype(), parameters);
super(other, parameters);
}
/**

Loading…
Cancel
Save