Polish ContentNegotiationStrategy support

Issue: SPR-8410, SPR-8417, SPR-8418,SPR-8416, SPR-8419,SPR-7722
This commit is contained in:
Rossen Stoyanchev
2012-06-25 15:24:05 -04:00
parent 4623568bce
commit f94aed8386
11 changed files with 80 additions and 61 deletions

View File

@@ -31,14 +31,14 @@ import org.springframework.web.context.request.NativeWebRequest;
* @author Rossen Stoyanchev
* @since 3.2
*/
public abstract class AbstractMappingContentNegotiationStrategy extends MappingMediaTypeExtensionsResolver
implements ContentNegotiationStrategy, MediaTypeExtensionsResolver {
public abstract class AbstractMappingContentNegotiationStrategy extends MappingMediaTypeFileExtensionResolver
implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver {
/**
* Create an instance with the given extension-to-MediaType lookup.
* @throws IllegalArgumentException if a media type string cannot be parsed
*/
public AbstractMappingContentNegotiationStrategy(Map<String, String> mediaTypes) {
public AbstractMappingContentNegotiationStrategy(Map<String, MediaType> mediaTypes) {
super(mediaTypes);
}

View File

@@ -33,28 +33,30 @@ import org.springframework.web.context.request.NativeWebRequest;
* in a request by delegating to a list of {@link ContentNegotiationStrategy} instances.
*
* <p>It may also be used to determine the extensions associated with a MediaType by
* delegating to a list of {@link MediaTypeExtensionsResolver} instances.
* delegating to a list of {@link MediaTypeFileExtensionResolver} instances.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class ContentNegotiationManager implements ContentNegotiationStrategy, MediaTypeExtensionsResolver {
public class ContentNegotiationManager implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver {
private final List<ContentNegotiationStrategy> contentNegotiationStrategies = new ArrayList<ContentNegotiationStrategy>();
private final List<ContentNegotiationStrategy> contentNegotiationStrategies =
new ArrayList<ContentNegotiationStrategy>();
private final Set<MediaTypeExtensionsResolver> extensionResolvers = new LinkedHashSet<MediaTypeExtensionsResolver>();
private final Set<MediaTypeFileExtensionResolver> fileExtensionResolvers =
new LinkedHashSet<MediaTypeFileExtensionResolver>();
/**
* Create an instance with the given ContentNegotiationStrategy instances.
* <p>Each instance is checked to see if it is also an implementation of
* MediaTypeExtensionsResolver, and if so it is registered as such.
* MediaTypeFileExtensionResolver, and if so it is registered as such.
*/
public ContentNegotiationManager(ContentNegotiationStrategy... strategies) {
Assert.notEmpty(strategies, "At least one ContentNegotiationStrategy is expected");
this.contentNegotiationStrategies.addAll(Arrays.asList(strategies));
for (ContentNegotiationStrategy strategy : this.contentNegotiationStrategies) {
if (strategy instanceof MediaTypeExtensionsResolver) {
this.extensionResolvers.add((MediaTypeExtensionsResolver) strategy);
if (strategy instanceof MediaTypeFileExtensionResolver) {
this.fileExtensionResolvers.add((MediaTypeFileExtensionResolver) strategy);
}
}
}
@@ -67,10 +69,10 @@ public class ContentNegotiationManager implements ContentNegotiationStrategy, Me
}
/**
* Add MediaTypeExtensionsResolver instances.
* Add MediaTypeFileExtensionResolver instances.
*/
public void addExtensionsResolver(MediaTypeExtensionsResolver... resolvers) {
this.extensionResolvers.addAll(Arrays.asList(resolvers));
public void addFileExtensionResolvers(MediaTypeFileExtensionResolver... resolvers) {
this.fileExtensionResolvers.addAll(Arrays.asList(resolvers));
}
/**
@@ -91,13 +93,13 @@ public class ContentNegotiationManager implements ContentNegotiationStrategy, Me
}
/**
* Delegate to all configured MediaTypeExtensionsResolver instances and aggregate
* the list of all extensions found.
* Delegate to all configured MediaTypeFileExtensionResolver instances and aggregate
* the list of all file extensions found.
*/
public List<String> resolveExtensions(MediaType mediaType) {
public List<String> resolveFileExtensions(MediaType mediaType) {
Set<String> extensions = new LinkedHashSet<String>();
for (MediaTypeExtensionsResolver resolver : this.extensionResolvers) {
extensions.addAll(resolver.resolveExtensions(mediaType));
for (MediaTypeFileExtensionResolver resolver : this.fileExtensionResolvers) {
extensions.addAll(resolver.resolveFileExtensions(mediaType));
}
return new ArrayList<String>(extensions);
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.web.accept;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -24,28 +24,32 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* An implementation of {@link MediaTypeExtensionsResolver} that maintains a lookup
* An implementation of {@link MediaTypeFileExtensionResolver} that maintains a lookup
* from extension to MediaType.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
public class MappingMediaTypeExtensionsResolver implements MediaTypeExtensionsResolver {
public class MappingMediaTypeFileExtensionResolver implements MediaTypeFileExtensionResolver {
private ConcurrentMap<String, MediaType> mediaTypes = new ConcurrentHashMap<String, MediaType>();
private final ConcurrentMap<String, MediaType> mediaTypes = new ConcurrentHashMap<String, MediaType>();
private final MultiValueMap<MediaType, String> fileExtensions = new LinkedMultiValueMap<MediaType, String>();
/**
* Create an instance with the given mappings between extensions and media types.
* @throws IllegalArgumentException if a media type string cannot be parsed
*/
public MappingMediaTypeExtensionsResolver(Map<String, String> mediaTypes) {
public MappingMediaTypeFileExtensionResolver(Map<String, MediaType> mediaTypes) {
if (mediaTypes != null) {
for (Map.Entry<String, String> entry : mediaTypes.entrySet()) {
String extension = entry.getKey().toLowerCase(Locale.ENGLISH);
MediaType mediaType = MediaType.parseMediaType(entry.getValue());
this.mediaTypes.put(extension, mediaType);
for (Entry<String, MediaType> entries : mediaTypes.entrySet()) {
String extension = entries.getKey().toLowerCase(Locale.ENGLISH);
MediaType mediaType = entries.getValue();
addMapping(extension, mediaType);
}
}
}
@@ -54,14 +58,9 @@ public class MappingMediaTypeExtensionsResolver implements MediaTypeExtensionsRe
* Find the extensions applicable to the given MediaType.
* @return 0 or more extensions, never {@code null}
*/
public List<String> resolveExtensions(MediaType mediaType) {
List<String> result = new ArrayList<String>();
for (Entry<String, MediaType> entry : this.mediaTypes.entrySet()) {
if (mediaType.includes(entry.getValue())) {
result.add(entry.getKey());
}
}
return result;
public List<String> resolveFileExtensions(MediaType mediaType) {
List<String> fileExtensions = this.fileExtensions.get(mediaType);
return (fileExtensions != null) ? fileExtensions : Collections.<String>emptyList();
}
/**
@@ -76,7 +75,10 @@ public class MappingMediaTypeExtensionsResolver implements MediaTypeExtensionsRe
* Map a MediaType to an extension or ignore if the extensions is already mapped.
*/
protected void addMapping(String extension, MediaType mediaType) {
this.mediaTypes.putIfAbsent(extension, mediaType);
MediaType previous = this.mediaTypes.putIfAbsent(extension, mediaType);
if (previous == null) {
this.fileExtensions.add(mediaType, extension);
}
}
}

View File

@@ -27,7 +27,7 @@ import org.springframework.http.MediaType;
* @author Rossen Stoyanchev
* @since 3.2
*/
public interface MediaTypeExtensionsResolver {
public interface MediaTypeFileExtensionResolver {
/**
* Resolve the given media type to a list of path extensions.
@@ -35,6 +35,6 @@ public interface MediaTypeExtensionsResolver {
* @param mediaType the media type to resolve
* @return a list of extensions or an empty list, never {@code null}
*/
List<String> resolveExtensions(MediaType mediaType);
List<String> resolveFileExtensions(MediaType mediaType);
}

View File

@@ -42,7 +42,7 @@ public class ParameterContentNegotiationStrategy extends AbstractMappingContentN
* Create an instance with the given extension-to-MediaType lookup.
* @throws IllegalArgumentException if a media type string cannot be parsed
*/
public ParameterContentNegotiationStrategy(Map<String, String> mediaTypes) {
public ParameterContentNegotiationStrategy(Map<String, MediaType> mediaTypes) {
super(mediaTypes);
Assert.notEmpty(mediaTypes, "Cannot look up media types without any mappings");
}

View File

@@ -72,7 +72,7 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
* Create an instance with the given extension-to-MediaType lookup.
* @throws IllegalArgumentException if a media type string cannot be parsed
*/
public PathExtensionContentNegotiationStrategy(Map<String, String> mediaTypes) {
public PathExtensionContentNegotiationStrategy(Map<String, MediaType> mediaTypes) {
super(mediaTypes);
}