diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java
index b22ae2c2ab2..883fcd0b682 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java
@@ -16,31 +16,19 @@
package org.springframework.web.servlet.mvc.method;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.PathMatcher;
-import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition;
import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition;
-import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
import org.springframework.web.servlet.mvc.method.condition.RequestMethodsRequestCondition;
/**
- * Contains a set of conditions to match to a given request such as URL patterns, HTTP methods, request parameters
- * and headers.
- *
- *
Two {@link RequestMappingInfo}s can be combined resulting in a new {@link RequestMappingInfo} with conditions
- * from both. A {@link RequestMappingInfo} can also match itself to an HTTP request resulting in a new
- * {@link RequestMappingInfo} with the subset of conditions relevant to the request.
+ * Contains request mapping conditions to be matched to a given request.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
@@ -48,7 +36,7 @@ import org.springframework.web.servlet.mvc.method.condition.RequestMethodsReques
*/
public final class RequestMappingInfo {
- private final Set patterns;
+ private final PatternsRequestCondition patternsCondition;
private final RequestMethodsRequestCondition methodsCondition;
@@ -63,24 +51,15 @@ public final class RequestMappingInfo {
private int hash;
/**
- * Creates a new {@code RequestMappingInfo} instance with the given URL patterns and HTTP methods.
- *
- * Package protected for testing purposes.
+ * Creates a new {@code RequestMappingInfo} instance.
*/
- RequestMappingInfo(Collection patterns, RequestMethod[] methods) {
- this(patterns, RequestConditionFactory.parseMethods(methods), null, null, null, null);
- }
-
- /**
- * Creates a new {@code RequestMappingInfo} instance with a full set of conditions.
- */
- public RequestMappingInfo(Collection patterns,
- RequestMethodsRequestCondition methodsCondition,
- ParamsRequestCondition paramsCondition,
- HeadersRequestCondition headersCondition,
- ConsumesRequestCondition consumesCondition,
- ProducesRequestCondition producesCondition) {
- this.patterns = asUnmodifiableSet(prependLeadingSlash(patterns));
+ public RequestMappingInfo(PatternsRequestCondition patternsCondition,
+ RequestMethodsRequestCondition methodsCondition,
+ ParamsRequestCondition paramsCondition,
+ HeadersRequestCondition headersCondition,
+ ConsumesRequestCondition consumesCondition,
+ ProducesRequestCondition producesCondition) {
+ this.patternsCondition = patternsCondition != null ? patternsCondition : new PatternsRequestCondition();
this.methodsCondition = methodsCondition != null ? methodsCondition : new RequestMethodsRequestCondition();
this.paramsCondition = paramsCondition != null ? paramsCondition : new ParamsRequestCondition();
this.headersCondition = headersCondition != null ? headersCondition : new HeadersRequestCondition();
@@ -88,67 +67,52 @@ public final class RequestMappingInfo {
this.producesCondition = producesCondition != null ? producesCondition : new ProducesRequestCondition();
}
- private static Set prependLeadingSlash(Collection patterns) {
- if (patterns == null) {
- return Collections.emptySet();
- }
- Set result = new LinkedHashSet(patterns.size());
- for (String pattern : patterns) {
- if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
- pattern = "/" + pattern;
- }
- result.add(pattern);
- }
- return result;
- }
-
- private static Set asUnmodifiableSet(Collection collection) {
- if (collection == null) {
- return Collections.emptySet();
- }
- Set result = new LinkedHashSet(collection);
- return Collections.unmodifiableSet(result);
+ /**
+ * Package protected, used for testing.
+ */
+ RequestMappingInfo(String[] patterns, RequestMethod... methods) {
+ this(new PatternsRequestCondition(patterns), new RequestMethodsRequestCondition(methods), null, null, null, null);
}
/**
* Returns the patterns of this request mapping info.
*/
- public Set getPatterns() {
- return patterns;
+ public PatternsRequestCondition getPatternsCondition() {
+ return patternsCondition;
}
/**
- * Returns the request method conditions of this request mapping info.
+ * Returns the request method condition of this request mapping info.
*/
- public RequestMethodsRequestCondition getMethods() {
+ public RequestMethodsRequestCondition getMethodsCondition() {
return methodsCondition;
}
/**
- * Returns the request parameters conditions of this request mapping info.
+ * Returns the request parameters condition of this request mapping info.
*/
- public ParamsRequestCondition getParams() {
+ public ParamsRequestCondition getParamsCondition() {
return paramsCondition;
}
/**
- * Returns the request headers conditions of this request mapping info.
+ * Returns the request headers condition of this request mapping info.
*/
- public HeadersRequestCondition getHeaders() {
+ public HeadersRequestCondition getHeadersCondition() {
return headersCondition;
}
/**
- * Returns the request consumes conditions of this request mapping info.
+ * Returns the request consumes condition of this request mapping info.
*/
- public ConsumesRequestCondition getConsumes() {
+ public ConsumesRequestCondition getConsumesCondition() {
return consumesCondition;
}
/**
- * Returns the request produces conditions of this request mapping info.
+ * Returns the request produces condition of this request mapping info.
*/
- public ProducesRequestCondition getProduces() {
+ public ProducesRequestCondition getProducesCondition() {
return producesCondition;
}
@@ -167,110 +131,44 @@ public final class RequestMappingInfo {
* Consumes are combined as per {@link ConsumesRequestCondition#combine(ConsumesRequestCondition)}.
*
* @param methodKey the key to combine with
- * @param pathMatcher to {@linkplain PathMatcher#combine(String, String) combine} the patterns
* @return a new request mapping info containing conditions from both keys
*/
- public RequestMappingInfo combine(RequestMappingInfo methodKey, PathMatcher pathMatcher) {
- Set patterns = combinePatterns(this.patterns, methodKey.patterns, pathMatcher);
+ public RequestMappingInfo combine(RequestMappingInfo methodKey) {
+ PatternsRequestCondition patterns = this.patternsCondition.combine(methodKey.patternsCondition);
RequestMethodsRequestCondition methods = this.methodsCondition.combine(methodKey.methodsCondition);
ParamsRequestCondition params = this.paramsCondition.combine(methodKey.paramsCondition);
HeadersRequestCondition headers = this.headersCondition.combine(methodKey.headersCondition);
ConsumesRequestCondition consumes = this.consumesCondition.combine(methodKey.consumesCondition);
ProducesRequestCondition produces = this.producesCondition.combine(methodKey.producesCondition);
-
+
return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces);
}
- private static Set combinePatterns(Collection typePatterns,
- Collection methodPatterns,
- PathMatcher pathMatcher) {
- Set result = new LinkedHashSet();
- if (!typePatterns.isEmpty() && !methodPatterns.isEmpty()) {
- for (String pattern1 : typePatterns) {
- for (String pattern2 : methodPatterns) {
- result.add(pathMatcher.combine(pattern1, pattern2));
- }
- }
- }
- else if (!typePatterns.isEmpty()) {
- result.addAll(typePatterns);
- }
- else if (!methodPatterns.isEmpty()) {
- result.addAll(methodPatterns);
- }
- else {
- result.add("");
- }
- return result;
- }
-
/**
- * Returns a new {@code RequestMappingInfo} that contains all conditions of this key that are relevant to the request.
- *
- * - The list of URL path patterns is trimmed to contain the patterns that match the URL with matching patterns
- * sorted via {@link PathMatcher#getPatternComparator(String)}.
- *
- The list of HTTP methods is trimmed to contain only the method of the request.
- *
- Request parameter and request header conditions are included in full.
- *
- The list of consumes conditions is trimmed and sorted to match the request "Content-Type" header.
- *
- * @param lookupPath mapping lookup path within the current servlet mapping if applicable
+ * Returns a new {@code RequestMappingInfo} with conditions relevant to the current request.
+ * For example the list of URL path patterns is trimmed to contain the patterns that match the URL.
* @param request the current request
- * @param pathMatcher to check for matching patterns
* @return a new request mapping info that contains all matching attributes, or {@code null} if not all conditions match
*/
- public RequestMappingInfo getMatchingRequestMapping(String lookupPath, HttpServletRequest request, PathMatcher pathMatcher) {
- RequestMethodsRequestCondition matchingMethodCondition = methodsCondition.getMatchingCondition(request);
- ParamsRequestCondition matchingParamsCondition = paramsCondition.getMatchingCondition(request);
- HeadersRequestCondition matchingHeadersCondition = headersCondition.getMatchingCondition(request);
- ConsumesRequestCondition matchingConsumesCondition = consumesCondition.getMatchingCondition(request);
- ProducesRequestCondition matchingProducesCondition = producesCondition.getMatchingCondition(request);
+ public RequestMappingInfo getMatchingRequestMapping(HttpServletRequest request) {
+ RequestMethodsRequestCondition matchingMethod = methodsCondition.getMatchingCondition(request);
+ ParamsRequestCondition matchingParams = paramsCondition.getMatchingCondition(request);
+ HeadersRequestCondition matchingHeaders = headersCondition.getMatchingCondition(request);
+ ConsumesRequestCondition matchingConsumes = consumesCondition.getMatchingCondition(request);
+ ProducesRequestCondition matchingProduces = producesCondition.getMatchingCondition(request);
- if (matchingMethodCondition == null || matchingParamsCondition == null || matchingHeadersCondition == null ||
- matchingConsumesCondition == null || matchingProducesCondition == null) {
+ if (matchingMethod == null || matchingParams == null || matchingHeaders == null ||
+ matchingConsumes == null || matchingProduces == null) {
return null;
}
- else {
- List matchingPatterns = getMatchingPatterns(lookupPath, pathMatcher);
- if (!matchingPatterns.isEmpty()) {
- return new RequestMappingInfo(matchingPatterns, matchingMethodCondition, matchingParamsCondition,
- matchingHeadersCondition, matchingConsumesCondition, matchingProducesCondition);
- }
- else {
- return null;
- }
- }
- }
-
- private List getMatchingPatterns(String lookupPath, PathMatcher pathMatcher) {
-
- List matchingPatterns = new ArrayList();
- for (String pattern : this.patterns) {
- String matchingPattern = getMatchingPattern(pattern, lookupPath, pathMatcher);
- if (matchingPattern != null) {
- matchingPatterns.add(matchingPattern);
- }
+
+ PatternsRequestCondition matchingPatterns = patternsCondition.getMatchingCondition(request);
+ if (matchingPatterns != null) {
+ return new RequestMappingInfo(matchingPatterns, matchingMethod,
+ matchingParams, matchingHeaders, matchingConsumes,
+ matchingProduces);
}
- Collections.sort(matchingPatterns, pathMatcher.getPatternComparator(lookupPath));
-
- return matchingPatterns;
- }
-
- private String getMatchingPattern(String pattern, String lookupPath, PathMatcher pathMatcher) {
- if (pattern.equals(lookupPath)) {
- return pattern;
- }
- boolean hasSuffix = pattern.indexOf('.') != -1;
- if (!hasSuffix && pathMatcher.match(pattern + ".*", lookupPath)) {
- return pattern + ".*";
- }
- if (pathMatcher.match(pattern, lookupPath)) {
- return pattern;
- }
- boolean endsWithSlash = pattern.endsWith("/");
- if (!endsWithSlash && pathMatcher.match(pattern + "/", lookupPath)) {
- return pattern +"/";
- }
return null;
}
@@ -281,7 +179,7 @@ public final class RequestMappingInfo {
}
if (obj != null && obj instanceof RequestMappingInfo) {
RequestMappingInfo other = (RequestMappingInfo) obj;
- return (this.patterns.equals(other.patterns) &&
+ return (this.patternsCondition.equals(other.patternsCondition) &&
this.methodsCondition.equals(other.methodsCondition) &&
this.paramsCondition.equals(other.paramsCondition) &&
this.headersCondition.equals(other.headersCondition) &&
@@ -295,7 +193,7 @@ public final class RequestMappingInfo {
public int hashCode() {
int result = hash;
if (result == 0) {
- result = patterns.hashCode();
+ result = patternsCondition.hashCode();
result = 31 * result + methodsCondition.hashCode();
result = 31 * result + paramsCondition.hashCode();
result = 31 * result + headersCondition.hashCode();
@@ -309,7 +207,7 @@ public final class RequestMappingInfo {
@Override
public String toString() {
StringBuilder builder = new StringBuilder("{");
- builder.append(patterns);
+ builder.append(patternsCondition);
builder.append(",methods=").append(methodsCondition);
builder.append(",params=").append(paramsCondition);
builder.append(",headers=").append(headersCondition);
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java
index 8a9118d4347..97fa811fff0 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.java
@@ -19,7 +19,6 @@ package org.springframework.web.servlet.mvc.method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -61,7 +60,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
@Override
protected Set getMappingPaths(RequestMappingInfo mapping) {
- return mapping.getPatterns();
+ return mapping.getPatternsCondition().getPatterns();
}
/**
@@ -73,7 +72,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo mapping,
String lookupPath,
HttpServletRequest request) {
- return mapping.getMatchingRequestMapping(lookupPath, request, getPathMatcher());
+ return mapping.getMatchingRequestMapping(request);
}
/**
@@ -81,7 +80,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
*/
@Override
protected Comparator getMappingComparator(String lookupPath, HttpServletRequest request) {
- return new RequestMappingInfoComparator(lookupPath, request);
+ return new RequestMappingInfoComparator(request);
}
/**
@@ -94,12 +93,12 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
super.handleMatch(info, lookupPath, request);
- String pattern = info.getPatterns().iterator().next();
+ String pattern = info.getPatternsCondition().getPatterns().iterator().next();
Map uriTemplateVariables = getPathMatcher().extractUriTemplateVariables(pattern, lookupPath);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
- if (!info.getProduces().isEmpty()) {
- Set mediaTypes = info.getProduces().getMediaTypes();
+ if (!info.getProducesCondition().isEmpty()) {
+ Set mediaTypes = info.getProducesCondition().getMediaTypes();
request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
@@ -117,18 +116,18 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
Set consumableMediaTypes = new HashSet();
Set producibleMediaTypes = new HashSet();
for (RequestMappingInfo info : requestMappingInfos) {
- for (String pattern : info.getPatterns()) {
+ for (String pattern : info.getPatternsCondition().getPatterns()) {
if (getPathMatcher().match(pattern, lookupPath)) {
- if (!info.getMethods().match(request)) {
- for (RequestMethod method : info.getMethods().getMethods()) {
+ if (info.getMethodsCondition().getMatchingCondition(request) == null) {
+ for (RequestMethod method : info.getMethodsCondition().getMethods()) {
allowedMethods.add(method.name());
}
}
- if (!info.getConsumes().match(request)) {
- consumableMediaTypes.addAll(info.getConsumes().getMediaTypes());
+ if (info.getConsumesCondition().getMatchingCondition(request) == null) {
+ consumableMediaTypes.addAll(info.getConsumesCondition().getMediaTypes());
}
- if (!info.getProduces().match(request)) {
- producibleMediaTypes.addAll(info.getProduces().getMediaTypes());
+ if (info.getProducesCondition().getMatchingCondition(request) == null) {
+ producibleMediaTypes.addAll(info.getProducesCondition().getMediaTypes());
}
}
}
@@ -152,77 +151,44 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
}
/**
- * A comparator for {@link RequestMappingInfo}s. Effective comparison can only be done in the context of a specific
- * request. For example not all {@link RequestMappingInfo} patterns may apply to the current request. Therefore an
- * HttpServletRequest is required as input.
- *
- * Furthermore, the following assumptions are made about the input RequestMappings:
- Each RequestMappingInfo
- * has been fully matched to the request
- The RequestMappingInfo contains matched patterns only
- Patterns are
- * ordered with the best matching pattern at the top
- *
- * @see RequestMappingInfoHandlerMapping#getMatchingMapping(RequestMappingInfo, String, HttpServletRequest)
+ * A comparator for {@link RequestMappingInfo}s. Effective comparison can only be done in the context
+ * of a specific request. For example only a subset of URL patterns may apply to the current request.
*/
private class RequestMappingInfoComparator implements Comparator {
- private Comparator patternComparator;
+ private final HttpServletRequest request;
- private List requestAcceptHeader;
-
- public RequestMappingInfoComparator(String lookupPath, HttpServletRequest request) {
- this.patternComparator = getPathMatcher().getPatternComparator(lookupPath);
- String acceptHeader = request.getHeader("Accept");
- this.requestAcceptHeader = MediaType.parseMediaTypes(acceptHeader);
- MediaType.sortByQualityValue(this.requestAcceptHeader);
+ public RequestMappingInfoComparator(HttpServletRequest request) {
+ this.request = request;
}
public int compare(RequestMappingInfo mapping, RequestMappingInfo otherMapping) {
- int result = comparePatterns(mapping.getPatterns(), otherMapping.getPatterns());
+ int result = mapping.getPatternsCondition().compareTo(otherMapping.getPatternsCondition(), request);
if (result != 0) {
return result;
}
- result = mapping.getParams().compareTo(otherMapping.getParams());
+ result = mapping.getParamsCondition().compareTo(otherMapping.getParamsCondition(), request);
if (result != 0) {
return result;
}
- result = mapping.getHeaders().compareTo(otherMapping.getHeaders());
+ result = mapping.getHeadersCondition().compareTo(otherMapping.getHeadersCondition(), request);
if (result != 0) {
return result;
}
- result = mapping.getConsumes().compareTo(otherMapping.getConsumes());
+ result = mapping.getConsumesCondition().compareTo(otherMapping.getConsumesCondition(), request);
if (result != 0) {
return result;
}
- result = mapping.getProduces().compareTo(otherMapping.getProduces(), this.requestAcceptHeader);
+ result = mapping.getProducesCondition().compareTo(otherMapping.getProducesCondition(), request);
if (result != 0) {
return result;
}
- result = mapping.getMethods().compareTo(otherMapping.getMethods());
+ result = mapping.getMethodsCondition().compareTo(otherMapping.getMethodsCondition(), request);
if (result != 0) {
return result;
}
return 0;
}
-
- private int comparePatterns(Set patterns, Set otherPatterns) {
- Iterator iterator = patterns.iterator();
- Iterator iteratorOther = otherPatterns.iterator();
- while (iterator.hasNext() && iteratorOther.hasNext()) {
- int result = patternComparator.compare(iterator.next(), iteratorOther.next());
- if (result != 0) {
- return result;
- }
- }
- if (iterator.hasNext()) {
- return -1;
- }
- else if (iteratorOther.hasNext()) {
- return 1;
- }
- else {
- return 0;
- }
- }
-
}
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java
index a0102faaa70..23afb1cf64e 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java
@@ -17,7 +17,6 @@
package org.springframework.web.servlet.mvc.method.annotation;
import java.lang.reflect.Method;
-import java.util.Arrays;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;
@@ -25,7 +24,12 @@ import org.springframework.util.PathMatcher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
-import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
+import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.PatternsRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.RequestMethodsRequestCondition;
/**
* A sub-class of {@link RequestMappingInfoHandlerMapping} that prepares {@link RequestMappingInfo}s
@@ -64,7 +68,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
RequestMapping typeAnnot = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnot != null) {
RequestMappingInfo typeMapping = createFromRequestMapping(typeAnnot);
- return typeMapping.combine(methodMapping, getPathMatcher());
+ return typeMapping.combine(methodMapping);
}
else {
return methodMapping;
@@ -75,13 +79,14 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
}
}
- private static RequestMappingInfo createFromRequestMapping(RequestMapping annotation) {
- return new RequestMappingInfo(Arrays.asList(annotation.value()),
- RequestConditionFactory.parseMethods(annotation.method()),
- RequestConditionFactory.parseParams(annotation.params()),
- RequestConditionFactory.parseHeaders(annotation.headers()),
- RequestConditionFactory.parseConsumes(annotation.consumes(), annotation.headers()),
- RequestConditionFactory.parseProduces(annotation.produces(), annotation.headers()));
+ private RequestMappingInfo createFromRequestMapping(RequestMapping annotation) {
+ return new RequestMappingInfo(
+ new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher()),
+ new RequestMethodsRequestCondition(annotation.method()),
+ new ParamsRequestCondition(annotation.params()),
+ new HeadersRequestCondition(annotation.headers()),
+ new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
+ new ProducesRequestCondition(annotation.produces(), annotation.headers()));
}
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueExpression.java
similarity index 75%
rename from org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java
rename to org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueExpression.java
index ea859b7039e..06d2e81be76 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueCondition.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/AbstractNameValueExpression.java
@@ -19,14 +19,15 @@ package org.springframework.web.servlet.mvc.method.condition;
import javax.servlet.http.HttpServletRequest;
/**
- * A condition that supports simple "name=value" style expressions as documented in {@link
- * org.springframework.web.bind.annotation.RequestMapping#params()} and {@link org.springframework.web.bind.annotation.RequestMapping#headers()}.
+ * Supports "name=value" style expressions as described in:
+ * {@link org.springframework.web.bind.annotation.RequestMapping#params()} and
+ * {@link org.springframework.web.bind.annotation.RequestMapping#headers()}.
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
* @since 3.1
*/
-abstract class AbstractNameValueCondition implements RequestCondition {
+abstract class AbstractNameValueExpression {
protected final String name;
@@ -34,7 +35,7 @@ abstract class AbstractNameValueCondition implements RequestCondition {
protected final boolean isNegated;
- AbstractNameValueCondition(String expression) {
+ AbstractNameValueExpression(String expression) {
int separator = expression.indexOf('=');
if (separator == -1) {
this.isNegated = expression.startsWith("!");
@@ -65,6 +66,20 @@ abstract class AbstractNameValueCondition implements RequestCondition {
protected abstract boolean matchValue(HttpServletRequest request);
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj != null && obj instanceof AbstractNameValueExpression) {
+ AbstractNameValueExpression> other = (AbstractNameValueExpression>) obj;
+ return ((this.name.equalsIgnoreCase(other.name)) &&
+ (this.value != null ? this.value.equals(other.value) : other.value == null) &&
+ this.isNegated == other.isNegated);
+ }
+ return false;
+ }
+
@Override
public int hashCode() {
int result = name.hashCode();
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java
index c2323191534..adefee10fc5 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestCondition.java
@@ -16,140 +16,194 @@
package org.springframework.web.servlet.mvc.method.condition;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition.HeaderExpression;
/**
- * Represents a collection of consumes conditions, typically obtained from {@link org.springframework.web.bind.annotation.RequestMapping#consumes()
- * @RequestMapping.consumes()}.
- *
+ * A logical disjunction (' || ') request condition to match requests against consumable media type expressions.
+ *
+ * For details on the syntax of the expressions see {@link RequestMapping#consumes()}. If the condition is
+ * created with 0 consumable media type expressions, it matches to every request.
+ *
+ *
This request condition is also capable of parsing header expressions specifically selecting 'Content-Type'
+ * header expressions and converting them to consumable media type expressions.
+ *
* @author Arjen Poutsma
- * @see RequestConditionFactory#parseConsumes(String...)
- * @see RequestConditionFactory#parseConsumes(String[], String[])
+ * @author Rossen Stoyanchev
* @since 3.1
*/
-public class ConsumesRequestCondition
- extends MediaTypesRequestCondition
- implements Comparable {
+public class ConsumesRequestCondition extends RequestConditionSupport {
- ConsumesRequestCondition(Collection conditions) {
- super(conditions);
- }
-
- ConsumesRequestCondition(String... consumes) {
- this(parseConditions(Arrays.asList(consumes)));
- }
-
- private static Set parseConditions(List consumes) {
- Set conditions = new LinkedHashSet(consumes.size());
- for (String consume : consumes) {
- conditions.add(new ConsumeRequestCondition(consume));
- }
- return conditions;
- }
+ private final List expressions;
/**
- * Creates a default set of consumes request conditions.
+ * Creates a {@link ConsumesRequestCondition} with the given consumable media type expressions.
+ * @param consumes the expressions to parse; if 0 the condition matches to every request
*/
- public ConsumesRequestCondition() {
- this(Collections.emptySet());
+ public ConsumesRequestCondition(String... consumes) {
+ this(consumes, null);
}
/**
- * Returns a new {@code RequestCondition} that contains all conditions of this key that match the request.
- *
- * @param request the request
- * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match
+ * Creates a {@link ConsumesRequestCondition} with the given header and consumes expressions.
+ * In addition to consume expressions, {@code "Content-Type"} header expressions are extracted
+ * and treated as consumable media type expressions.
+ * @param consumes the consumes expressions to parse; 0 matches to all requests
+ * @param headers the header expression to parse; 0 matches to all requests
+ */
+ public ConsumesRequestCondition(String[] consumes, String[] headers) {
+ this(parseExpressions(consumes, headers));
+ }
+
+ /**
+ * Private constructor.
+ */
+ private ConsumesRequestCondition(Collection expressions) {
+ this.expressions = new ArrayList(expressions);
+ Collections.sort(this.expressions);
+ }
+
+ private static Set parseExpressions(String[] consumes, String[] headers) {
+ Set result = new LinkedHashSet();
+ if (headers != null) {
+ for (String header : headers) {
+ HeaderExpression expr = new HeaderExpression(header);
+ if ("Content-Type".equalsIgnoreCase(expr.name)) {
+ for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
+ result.add(new ConsumeMediaTypeExpression(mediaType, expr.isNegated));
+ }
+ }
+ }
+ }
+ if (consumes != null) {
+ for (String consume : consumes) {
+ result.add(new ConsumeMediaTypeExpression(consume));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the consumable media types contained in all expressions of this condition.
+ */
+ public Set getMediaTypes() {
+ Set result = new LinkedHashSet();
+ for (ConsumeMediaTypeExpression expression : expressions) {
+ result.add(expression.getMediaType());
+ }
+ return result;
+ }
+
+ /**
+ * Returns true if this condition contains 0 consumable media type expressions.
+ */
+ public boolean isEmpty() {
+ return expressions.isEmpty();
+ }
+
+ @Override
+ protected Collection getContent() {
+ return expressions;
+ }
+
+ @Override
+ protected boolean isLogicalConjunction() {
+ return false;
+ }
+
+ /**
+ * Returns the "other" instance as long as it contains any expressions; or "this" instance otherwise.
+ * In other words "other" takes precedence over "this" as long as it has expressions.
+ * Example: method-level "consumes" overrides type-level "consumes" condition.
+ */
+ public ConsumesRequestCondition combine(ConsumesRequestCondition other) {
+ return !other.expressions.isEmpty() ? other : this;
+ }
+
+ /**
+ * Checks if any of the consumable media type expressions match the given request and returns an instance that
+ * is guaranteed to contain matching media type expressions only.
+ *
+ * @param request the current request
+ *
+ * @return the same instance if the condition contains no expressions;
+ * or a new condition with matching expressions; or {@code null} if no expressions match.
*/
public ConsumesRequestCondition getMatchingCondition(HttpServletRequest request) {
if (isEmpty()) {
return this;
}
- Set matchingConditions = new LinkedHashSet(getConditions());
- for (Iterator iterator = matchingConditions.iterator(); iterator.hasNext();) {
- ConsumeRequestCondition condition = iterator.next();
- if (!condition.match(request)) {
+ Set result = new LinkedHashSet(expressions);
+ for (Iterator iterator = result.iterator(); iterator.hasNext();) {
+ ConsumeMediaTypeExpression expression = iterator.next();
+ if (!expression.match(request)) {
iterator.remove();
}
}
- if (matchingConditions.isEmpty()) {
- return null;
+ return (result.isEmpty()) ? null : new ConsumesRequestCondition(result);
+ }
+
+ /**
+ * Returns:
+ *
+ * - 0 if the two conditions have the same number of expressions
+ *
- Less than 1 if "this" has more in number or more specific consumable media type expressions
+ *
- Greater than 1 if "other" has more in number or more specific consumable media type expressions
+ *
+ *
+ * It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)}
+ * and each instance contains the matching consumable media type expression only or is otherwise empty.
+ */
+ public int compareTo(ConsumesRequestCondition other, HttpServletRequest request) {
+ if (expressions.isEmpty() && other.expressions.isEmpty()) {
+ return 0;
+ }
+ else if (expressions.isEmpty()) {
+ return 1;
+ }
+ else if (other.expressions.isEmpty()) {
+ return -1;
}
else {
- return new ConsumesRequestCondition(matchingConditions);
+ return expressions.get(0).compareTo(other.expressions.get(0));
}
}
/**
- * Combines this collection of request condition with another. Returns {@code other}, unless it is empty.
- *
- * @param other the condition to combine with
+ * Parsing and request matching logic for consumable media type expressions.
+ * @see RequestMapping#consumes()
*/
- public ConsumesRequestCondition combine(ConsumesRequestCondition other) {
- return !other.isEmpty() ? other : this;
- }
+ static class ConsumeMediaTypeExpression extends MediaTypeExpression {
-
- public int compareTo(ConsumesRequestCondition other) {
- MediaTypeRequestCondition thisMostSpecificCondition = this.getMostSpecificCondition();
- MediaTypeRequestCondition otherMostSpecificCondition = other.getMostSpecificCondition();
- if (thisMostSpecificCondition == null && otherMostSpecificCondition == null) {
- return 0;
- }
- else if (thisMostSpecificCondition == null) {
- return 1;
- }
- else if (otherMostSpecificCondition == null) {
- return -1;
- }
- else {
- return thisMostSpecificCondition.compareTo(otherMostSpecificCondition);
- }
- }
-
- private MediaTypeRequestCondition getMostSpecificCondition() {
- if (!isEmpty()) {
- return getSortedConditions().get(0);
- }
- else {
- return null;
- }
- }
-
-
- static class ConsumeRequestCondition extends MediaTypesRequestCondition.MediaTypeRequestCondition {
-
- ConsumeRequestCondition(String expression) {
+ ConsumeMediaTypeExpression(String expression) {
super(expression);
}
- ConsumeRequestCondition(MediaType mediaType, boolean negated) {
+ ConsumeMediaTypeExpression(MediaType mediaType, boolean negated) {
super(mediaType, negated);
}
@Override
protected boolean match(HttpServletRequest request, MediaType mediaType) {
- MediaType contentType = getContentType(request);
+
+ MediaType contentType = StringUtils.hasLength(request.getContentType()) ?
+ MediaType.parseMediaType(request.getContentType()) :
+ MediaType.APPLICATION_OCTET_STREAM ;
+
return mediaType.includes(contentType);
}
-
- private MediaType getContentType(HttpServletRequest request) {
- if (StringUtils.hasLength(request.getContentType())) {
- return MediaType.parseMediaType(request.getContentType());
- }
- else {
- return MediaType.APPLICATION_OCTET_STREAM;
- }
- }
-
}
+
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java
index bb41c829098..559e0e1ceb9 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestCondition.java
@@ -16,79 +16,117 @@
package org.springframework.web.servlet.mvc.method.condition;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
+
import javax.servlet.http.HttpServletRequest;
+import org.springframework.web.bind.annotation.RequestMapping;
+
/**
- * Represents a collection of header request conditions, typically obtained from {@link
- * org.springframework.web.bind.annotation.RequestMapping#headers() @RequestMapping.headers()}.
- *
+ * A logical conjunction (' && ') request condition that matches a request against a set of header expressions.
+ *
+ *
For details on the syntax of the expressions see {@link RequestMapping#headers()}. If the condition is
+ * created with 0 header expressions, it will match to every request.
+ *
+ *
Note: when parsing header expressions, {@code "Accept"} and {@code "Content-Type"} header expressions
+ * are filtered out. Those should be converted and used as "produces" and "consumes" conditions instead.
+ * See the constructors for {@link ProducesRequestCondition} and {@link ConsumesRequestCondition}.
+ *
* @author Arjen Poutsma
- * @see RequestConditionFactory#parseHeaders(String...)
+ * @author Rossen Stoyanchev
* @since 3.1
*/
-public class HeadersRequestCondition
- extends LogicalConjunctionRequestCondition
- implements Comparable {
+public class HeadersRequestCondition extends RequestConditionSupport {
- HeadersRequestCondition(Collection conditions) {
- super(conditions);
+ private final Set expressions;
+
+ /**
+ * Create a {@link HeadersRequestCondition} with the given header expressions.
+ *
+ * Note: {@code "Accept"} and {@code "Content-Type"} header expressions are filtered out.
+ * Those should be converted and used as "produces" and "consumes" conditions instead.
+ * See the constructors for {@link ProducesRequestCondition} and {@link ConsumesRequestCondition}.
+ *
+ * @param headers 0 or more header expressions; if 0 the condition will match to every request.
+ */
+ public HeadersRequestCondition(String... headers) {
+ this(parseExpressions(headers));
}
-
- HeadersRequestCondition(String... headers) {
- this(parseConditions(Arrays.asList(headers)));
+
+ private HeadersRequestCondition(Collection conditions) {
+ this.expressions = Collections.unmodifiableSet(new LinkedHashSet(conditions));
}
-
- private static Set parseConditions(Collection params) {
- Set conditions = new LinkedHashSet(params.size());
- for (String param : params) {
- conditions.add(new HeaderRequestCondition(param));
+
+ private static Collection parseExpressions(String... headers) {
+ Set expressions = new LinkedHashSet();
+ if (headers != null) {
+ for (String header : headers) {
+ HeaderExpression expr = new HeaderExpression(header);
+ if ("Accept".equalsIgnoreCase(expr.name) || "Content-Type".equalsIgnoreCase(expr.name)) {
+ continue;
+ }
+ expressions.add(expr);
+ }
}
- return conditions;
+ return expressions;
+ }
+
+ @Override
+ protected Collection getContent() {
+ return expressions;
+ }
+
+ @Override
+ protected boolean isLogicalConjunction() {
+ return true;
}
/**
- * Creates an empty set of header request conditions.
- */
- public HeadersRequestCondition() {
- this(Collections.emptySet());
- }
-
- /**
- * Returns a new {@code RequestCondition} that contains all conditions that match the request.
- *
- * @param request the request
- * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match
- */
- public HeadersRequestCondition getMatchingCondition(HttpServletRequest request) {
- return match(request) ? this : null;
- }
-
-
- /**
- * Combines this collection of request condition with another by combining all header request conditions into a
- * logical AND.
- *
- * @param other the condition to combine with
+ * Returns a new instance with the union of the header expressions from "this" and the "other" instance.
*/
public HeadersRequestCondition combine(HeadersRequestCondition other) {
- Set conditions = new LinkedHashSet(getConditions());
- conditions.addAll(other.getConditions());
- return new HeadersRequestCondition(conditions);
+ Set set = new LinkedHashSet(this.expressions);
+ set.addAll(other.expressions);
+ return new HeadersRequestCondition(set);
+ }
+
+ /**
+ * Returns "this" instance if the request matches to all header expressions; or {@code null} otherwise.
+ */
+ public HeadersRequestCondition getMatchingCondition(HttpServletRequest request) {
+ for (HeaderExpression expression : expressions) {
+ if (!expression.match(request)) {
+ return null;
+ }
+ }
+ return this;
}
-
- public int compareTo(HeadersRequestCondition other) {
- return other.getConditions().size() - this.getConditions().size();
+ /**
+ * Returns:
+ *
+ * - 0 if the two conditions have the same number of header expressions
+ *
- Less than 1 if "this" instance has more header expressions
+ *
- Greater than 1 if the "other" instance has more header expressions
+ *
+ *
+ * It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)}
+ * and each instance contains the matching header expression only or is otherwise empty.
+ */
+ public int compareTo(HeadersRequestCondition other, HttpServletRequest request) {
+ return other.expressions.size() - this.expressions.size();
}
- static class HeaderRequestCondition extends AbstractNameValueCondition {
+ /**
+ * Parsing and request matching logic for header expressions.
+ * @see RequestMapping#headers()
+ */
+ static class HeaderExpression extends AbstractNameValueExpression {
- public HeaderRequestCondition(String expression) {
+ public HeaderExpression(String expression) {
super(expression);
}
@@ -107,20 +145,6 @@ public class HeadersRequestCondition
return value.equals(request.getHeader(name));
}
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj != null && obj instanceof HeaderRequestCondition) {
- HeaderRequestCondition other = (HeaderRequestCondition) obj;
- return ((this.name.equalsIgnoreCase(other.name)) &&
- (this.value != null ? this.value.equals(other.value) : other.value == null) &&
- this.isNegated == other.isNegated);
- }
- return false;
- }
-
@Override
public int hashCode() {
int result = name.toLowerCase().hashCode();
@@ -128,7 +152,5 @@ public class HeadersRequestCondition
result = 31 * result + (isNegated ? 1 : 0);
return result;
}
-
-
- }
+ }
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java
deleted file mode 100644
index 7873d909628..00000000000
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalConjunctionRequestCondition.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2002-2011 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.web.servlet.mvc.method.condition;
-
-import java.util.Collection;
-import java.util.Set;
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * {@link RequestCondition} implementation that represents a logical AND (i.e. &&).
- *
- * @author Arjen Poutsma
- * @since 3.1
- */
-class LogicalConjunctionRequestCondition extends RequestConditionComposite {
-
- LogicalConjunctionRequestCondition(Collection conditions) {
- super(conditions);
- }
-
- public boolean match(HttpServletRequest request) {
- Set conditions = getConditions();
- if (isEmpty()) {
- return true;
- }
- for (T condition : conditions) {
- if (!condition.match(request)) {
- return false;
- }
- }
- return true;
- }
-
-
- @Override
- protected String getToStringInfix() {
- return " && ";
- }
-
-}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java
deleted file mode 100644
index e775a76c96a..00000000000
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/LogicalDisjunctionRequestCondition.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2002-2011 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.web.servlet.mvc.method.condition;
-
-import java.util.Collection;
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * {@link RequestCondition} implementation that represents a logical OR (i.e. ||).
- *
- * @author Arjen Poutsma
- * @since 3.1
- */
-class LogicalDisjunctionRequestCondition extends RequestConditionComposite {
-
- LogicalDisjunctionRequestCondition(Collection conditions) {
- super(conditions);
- }
-
- public boolean match(HttpServletRequest request) {
- if (isEmpty()) {
- return true;
- }
- for (RequestCondition condition : getConditions()) {
- if (condition.match(request)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- protected String getToStringInfix() {
- return " || ";
- }
-
-}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeExpression.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeExpression.java
new file mode 100644
index 00000000000..ca24662ba5d
--- /dev/null
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypeExpression.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2002-2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.servlet.mvc.method.condition;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * Supports media type expressions as described in:
+ * {@link RequestMapping#consumes()} and {@link RequestMapping#produces()}.
+ *
+ * @author Arjen Poutsma
+ * @author Rossen Stoyanchev
+ * @since 3.1
+ */
+abstract class MediaTypeExpression implements Comparable {
+
+ private final MediaType mediaType;
+
+ private final boolean isNegated;
+
+ MediaTypeExpression(String expression) {
+ if (expression.startsWith("!")) {
+ isNegated = true;
+ expression = expression.substring(1);
+ }
+ else {
+ isNegated = false;
+ }
+ this.mediaType = MediaType.parseMediaType(expression);
+ }
+
+ MediaTypeExpression(MediaType mediaType, boolean negated) {
+ this.mediaType = mediaType;
+ isNegated = negated;
+ }
+
+ public boolean match(HttpServletRequest request) {
+ boolean match = match(request, this.mediaType);
+ return !isNegated ? match : !match;
+ }
+
+ protected abstract boolean match(HttpServletRequest request, MediaType mediaType);
+
+ MediaType getMediaType() {
+ return mediaType;
+ }
+
+ public int compareTo(MediaTypeExpression other) {
+ return MediaType.SPECIFICITY_COMPARATOR.compare(this.getMediaType(), other.getMediaType());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj != null && getClass().equals(obj.getClass())) {
+ MediaTypeExpression other = (MediaTypeExpression) obj;
+ return (this.mediaType.equals(other.mediaType)) && (this.isNegated == other.isNegated);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mediaType.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ if (isNegated) {
+ builder.append('!');
+ }
+ builder.append(mediaType.toString());
+ return builder.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypesRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypesRequestCondition.java
deleted file mode 100644
index df254e0741a..00000000000
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/MediaTypesRequestCondition.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2002-2011 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.web.servlet.mvc.method.condition;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import javax.servlet.http.HttpServletRequest;
-
-import org.springframework.http.MediaType;
-
-/**
- * @author Arjen Poutsma
- */
-class MediaTypesRequestCondition
- extends LogicalDisjunctionRequestCondition {
-
- private final List sortedConditions;
-
- public MediaTypesRequestCondition(Collection conditions) {
- super(conditions);
- sortedConditions = new ArrayList(conditions);
- Collections.sort(sortedConditions);
- }
-
- protected List getSortedConditions() {
- return sortedConditions;
- }
-
- /**
- * Returns all {@link MediaType}s contained in this condition.
- */
- public Set getMediaTypes() {
- Set result = new LinkedHashSet();
- for (MediaTypeRequestCondition condition : getConditions()) {
- result.add(condition.getMediaType());
- }
- return result;
- }
-
- /**
- * @author Arjen Poutsma
- */
- protected abstract static class MediaTypeRequestCondition
- implements RequestCondition, Comparable {
-
- private final MediaType mediaType;
-
- private final boolean isNegated;
-
- MediaTypeRequestCondition(MediaType mediaType, boolean negated) {
- this.mediaType = mediaType;
- isNegated = negated;
- }
-
- MediaTypeRequestCondition(String expression) {
- if (expression.startsWith("!")) {
- isNegated = true;
- expression = expression.substring(1);
- }
- else {
- isNegated = false;
- }
- this.mediaType = MediaType.parseMediaType(expression);
- }
-
- public boolean match(HttpServletRequest request) {
- boolean match = match(request, this.mediaType);
- return !isNegated ? match : !match;
- }
-
- protected abstract boolean match(HttpServletRequest request, MediaType mediaType);
-
- MediaType getMediaType() {
- return mediaType;
- }
-
- public int compareTo(MediaTypeRequestCondition other) {
- return MediaType.SPECIFICITY_COMPARATOR.compare(this.getMediaType(), other.getMediaType());
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj != null && getClass().equals(obj.getClass())) {
- MediaTypeRequestCondition other = (MediaTypeRequestCondition) obj;
- return (this.mediaType.equals(other.mediaType)) && (this.isNegated == other.isNegated);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return mediaType.hashCode();
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- if (isNegated) {
- builder.append('!');
- }
- builder.append(mediaType.toString());
- return builder.toString();
- }
- }
-}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java
index fba088f8ed7..7bbcf7c85ef 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ParamsRequestCondition.java
@@ -16,80 +16,106 @@
package org.springframework.web.servlet.mvc.method.condition;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
-import java.util.List;
import java.util.Set;
+
import javax.servlet.http.HttpServletRequest;
+import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.util.WebUtils;
/**
- * Represents a collection of parameter request conditions, typically obtained from {@link
- * org.springframework.web.bind.annotation.RequestMapping#params() @RequestMapping.params()}.
- *
+ * A logical conjunction (' && ') request condition that matches a request against a set parameter expressions.
+ *
+ * For details on the syntax of the expressions see {@link RequestMapping#params()}. If the condition is
+ * created with 0 parameter expressions, it will match to every request.
+ *
* @author Arjen Poutsma
- * @see RequestConditionFactory#parseParams(String...)
+ * @author Rossen Stoyanchev
* @since 3.1
*/
-public class ParamsRequestCondition
- extends LogicalConjunctionRequestCondition
- implements Comparable {
+public class ParamsRequestCondition extends RequestConditionSupport {
- private ParamsRequestCondition(Collection conditions) {
- super(conditions);
+ private final Set expressions;
+
+ /**
+ * Create a {@link ParamsRequestCondition} with the given param expressions.
+ *
+ * @param params 0 or more param expressions; if 0 the condition will match to every request.
+ */
+ public ParamsRequestCondition(String... params) {
+ this(parseExpressions(params));
+ }
+
+ private ParamsRequestCondition(Collection conditions) {
+ this.expressions = Collections.unmodifiableSet(new LinkedHashSet(conditions));
}
- ParamsRequestCondition(String... params) {
- this(parseConditions(Arrays.asList(params)));
- }
-
- private static Set parseConditions(List params) {
- Set conditions = new LinkedHashSet(params.size());
- for (String param : params) {
- conditions.add(new ParamRequestCondition(param));
+ private static Collection parseExpressions(String... params) {
+ Set expressions = new LinkedHashSet();
+ if (params != null) {
+ for (String header : params) {
+ expressions.add(new ParamExpression(header));
+ }
}
- return conditions;
+ return expressions;
+ }
+
+ @Override
+ protected Collection getContent() {
+ return expressions;
+ }
+
+ @Override
+ protected boolean isLogicalConjunction() {
+ return true;
}
/**
- * Creates an empty set of parameter request conditions.
- */
- public ParamsRequestCondition() {
- this(Collections.emptySet());
- }
-
- /**
- * Returns a new {@code RequestCondition} that contains all conditions that match the request.
- *
- * @param request the request
- * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match
- */
- public ParamsRequestCondition getMatchingCondition(HttpServletRequest request) {
- return match(request) ? this : null;
- }
-
- /**
- * Combines this collection of request condition with another by combining all parameter request conditions into a
- * logical AND.
- *
- * @param other the condition to combine with
+ * Returns a new instance with the union of the param expressions from "this" and the "other" instance.
*/
public ParamsRequestCondition combine(ParamsRequestCondition other) {
- Set conditions = new LinkedHashSet(getConditions());
- conditions.addAll(other.getConditions());
- return new ParamsRequestCondition(conditions);
+ Set set = new LinkedHashSet(this.expressions);
+ set.addAll(other.expressions);
+ return new ParamsRequestCondition(set);
}
- public int compareTo(ParamsRequestCondition other) {
- return other.getConditions().size() - this.getConditions().size();
+ /**
+ * Returns "this" instance if the request matches to all parameter expressions; or {@code null} otherwise.
+ */
+ public ParamsRequestCondition getMatchingCondition(HttpServletRequest request) {
+ for (ParamExpression expression : expressions) {
+ if (!expression.match(request)) {
+ return null;
+ }
+ }
+ return this;
}
- static class ParamRequestCondition extends AbstractNameValueCondition {
+ /**
+ * Returns:
+ *
+ * - 0 if the two conditions have the same number of parameter expressions
+ *
- Less than 1 if "this" instance has more parameter expressions
+ *
- Greater than 1 if the "other" instance has more parameter expressions
+ *
+ *
+ * It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)}
+ * and each instance contains the matching parameter expressions only or is otherwise empty.
+ */
+ public int compareTo(ParamsRequestCondition other, HttpServletRequest request) {
+ return other.expressions.size() - this.expressions.size();
+ }
- ParamRequestCondition(String expression) {
+ /**
+ * Parsing and request matching logic for parameter expressions.
+ * @see RequestMapping#params()
+ */
+ static class ParamExpression extends AbstractNameValueExpression {
+
+ ParamExpression(String expression) {
super(expression);
}
@@ -107,20 +133,6 @@ public class ParamsRequestCondition
protected boolean matchValue(HttpServletRequest request) {
return value.equals(request.getParameter(name));
}
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj != null && obj instanceof ParamRequestCondition) {
- ParamRequestCondition other = (ParamRequestCondition) obj;
- return ((this.name.equals(other.name)) &&
- (this.value != null ? this.value.equals(other.value) : other.value == null) &&
- this.isNegated == other.isNegated);
- }
- return false;
- }
-
}
+
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/PatternsRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/PatternsRequestCondition.java
new file mode 100644
index 00000000000..339128145d6
--- /dev/null
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/PatternsRequestCondition.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2002-2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.servlet.mvc.method.condition;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+import org.springframework.util.StringUtils;
+import org.springframework.web.util.UrlPathHelper;
+
+/**
+ * A logical disjunction (' || ') request condition that matches a request against a set of URL path patterns.
+ *
+ * See Javadoc on individual methods for details on how URL patterns are matched, combined, and compared.
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.1
+ */
+public class PatternsRequestCondition extends RequestConditionSupport {
+
+ private final Set patterns;
+
+ private final UrlPathHelper urlPathHelper;
+
+ private final PathMatcher pathMatcher;
+
+ /**
+ * Creates a new {@link PatternsRequestCondition} with the given URL patterns.
+ * Each pattern that is not empty and does not start with "/" is prepended with "/".
+ * @param patterns 0 or more URL patterns; if 0 the condition will match to every request.
+ */
+ public PatternsRequestCondition(String... patterns) {
+ this(patterns, new UrlPathHelper(), new AntPathMatcher());
+ }
+
+ /**
+ * Creates a new {@link PatternsRequestCondition} with the given URL patterns.
+ * Each pattern that is not empty and does not start with "/" is prepended with "/".
+ *
+ * @param patterns the URL patterns to use; if 0 the condition will match to every request.
+ * @param urlPathHelper a {@link UrlPathHelper} for determining the lookup path for a request
+ * @param pathMatcher a {@link PathMatcher} for pattern path matching
+ */
+ public PatternsRequestCondition(String[] patterns, UrlPathHelper urlPathHelper, PathMatcher pathMatcher) {
+ this(asList(patterns), urlPathHelper, pathMatcher);
+ }
+
+ private static List asList(String... patterns) {
+ return patterns != null ? Arrays.asList(patterns) : Collections.emptyList();
+ }
+
+ /**
+ * Private constructor.
+ */
+ private PatternsRequestCondition(Collection patterns, UrlPathHelper urlPathHelper, PathMatcher pathMatcher) {
+ this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
+ this.urlPathHelper = urlPathHelper;
+ this.pathMatcher = pathMatcher;
+ }
+
+ private static Set prependLeadingSlash(Collection patterns) {
+ if (patterns == null) {
+ return Collections.emptySet();
+ }
+ Set result = new LinkedHashSet(patterns.size());
+ for (String pattern : patterns) {
+ if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
+ pattern = "/" + pattern;
+ }
+ result.add(pattern);
+ }
+ return result;
+ }
+
+ public Set getPatterns() {
+ return patterns;
+ }
+
+ @Override
+ protected Collection getContent() {
+ return patterns;
+ }
+
+ @Override
+ protected boolean isLogicalConjunction() {
+ return false;
+ }
+
+ /**
+ * Returns a new instance with URL patterns from the current instance ("this") and
+ * the "other" instance as follows:
+ *
+ * - If there are patterns in both instances, combine the patterns in "this" with
+ * the patterns in "other" using {@link PathMatcher#combine(String, String)}.
+ *
- If only one instance has patterns, use them.
+ *
- If neither instance has patterns, use "".
+ *
+ */
+ public PatternsRequestCondition combine(PatternsRequestCondition other) {
+ Set result = new LinkedHashSet();
+ if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) {
+ for (String pattern1 : this.patterns) {
+ for (String pattern2 : other.patterns) {
+ result.add(pathMatcher.combine(pattern1, pattern2));
+ }
+ }
+ }
+ else if (!this.patterns.isEmpty()) {
+ result.addAll(this.patterns);
+ }
+ else if (!other.patterns.isEmpty()) {
+ result.addAll(other.patterns);
+ }
+ else {
+ result.add("");
+ }
+ return new PatternsRequestCondition(result, urlPathHelper, pathMatcher);
+ }
+
+ /**
+ * Checks if any of the patterns match the given request and returns an instance that is guaranteed
+ * to contain matching patterns, sorted via {@link PathMatcher#getPatternComparator(String)}.
+ *
+ * A matching pattern is obtained by making checks in the following order:
+ *
+ * - Direct match
+ *
- A pattern match with ".*" appended assuming the pattern already doesn't contain "."
+ *
- A pattern match
+ *
- A pattern match with "/" appended assuming the patterns already end with "/"
+ *
+ *
+ * @param request the current request
+ *
+ * @return the same instance if the condition contains no patterns;
+ * or a new condition with sorted matching patterns; or {@code null} if no patterns match.
+ */
+ public PatternsRequestCondition getMatchingCondition(HttpServletRequest request) {
+ if (patterns.isEmpty()) {
+ return this;
+ }
+ String lookupPath = urlPathHelper.getLookupPathForRequest(request);
+ List matches = new ArrayList();
+ for (String pattern : patterns) {
+ String match = getMatchingPattern(pattern, lookupPath);
+ if (match != null) {
+ matches.add(match);
+ }
+ }
+ Collections.sort(matches, pathMatcher.getPatternComparator(lookupPath));
+ return matches.isEmpty() ? null : new PatternsRequestCondition(matches, urlPathHelper, pathMatcher);
+ }
+
+ private String getMatchingPattern(String pattern, String lookupPath) {
+ if (pattern.equals(lookupPath)) {
+ return pattern;
+ }
+ boolean hasSuffix = pattern.indexOf('.') != -1;
+ if (!hasSuffix && pathMatcher.match(pattern + ".*", lookupPath)) {
+ return pattern + ".*";
+ }
+ if (pathMatcher.match(pattern, lookupPath)) {
+ return pattern;
+ }
+ boolean endsWithSlash = pattern.endsWith("/");
+ if (!endsWithSlash && pathMatcher.match(pattern + "/", lookupPath)) {
+ return pattern +"/";
+ }
+ return null;
+ }
+
+ /**
+ * Compare the two conditions and return 0 if they match equally to the request, less than one if "this"
+ * matches the request more, and greater than one if "other" matches the request more. Patterns are
+ * compared one at a time, from top to bottom via {@link PathMatcher#getPatternComparator(String)}.
+ * If all compared patterns match equally, but one instance has more patterns, it is a closer match.
+ *
+ * It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)}
+ * to ensure they contain only patterns that match the request and are sorted with the best matches on top.
+ */
+ public int compareTo(PatternsRequestCondition other, HttpServletRequest request) {
+ String lookupPath = urlPathHelper.getLookupPathForRequest(request);
+ Comparator patternComparator = pathMatcher.getPatternComparator(lookupPath);
+
+ Iterator iterator = patterns.iterator();
+ Iterator iteratorOther = other.patterns.iterator();
+ while (iterator.hasNext() && iteratorOther.hasNext()) {
+ int result = patternComparator.compare(iterator.next(), iteratorOther.next());
+ if (result != 0) {
+ return result;
+ }
+ }
+ if (iterator.hasNext()) {
+ return -1;
+ }
+ else if (iteratorOther.hasNext()) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+
+}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestCondition.java
index 37766fd2620..caade1ca522 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestCondition.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/ProducesRequestCondition.java
@@ -16,99 +16,170 @@
package org.springframework.web.servlet.mvc.method.condition;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition.HeaderExpression;
/**
- * Represents a collection of produces conditions, typically obtained from {@link
- * org.springframework.web.bind.annotation.RequestMapping#produces() @RequestMapping.produces()}.
- *
+ * A logical disjunction (' || ') request condition to match requests against producible media type expressions.
+ *
+ * For details on the syntax of the expressions see {@link RequestMapping#consumes()}. If the condition is
+ * created with 0 producible media type expressions, it matches to every request.
+ *
+ *
This request condition is also capable of parsing header expressions specifically selecting 'Accept' header
+ * expressions and converting them to prodicuble media type expressions.
+ *
* @author Arjen Poutsma
- * @see RequestConditionFactory#parseProduces(String...)
- * @see RequestConditionFactory#parseProduces(String[], String[])
+ * @author Rossen Stoyanchev
* @since 3.1
*/
-public class ProducesRequestCondition
- extends MediaTypesRequestCondition {
+public class ProducesRequestCondition extends RequestConditionSupport {
- ProducesRequestCondition(Collection conditions) {
- super(conditions);
- }
-
-
- ProducesRequestCondition(String... consumes) {
- this(parseConditions(Arrays.asList(consumes)));
- }
-
- private static Set parseConditions(List consumes) {
- Set conditions = new LinkedHashSet(consumes.size());
- for (String consume : consumes) {
- conditions.add(new ProduceRequestCondition(consume));
- }
- return conditions;
- }
+ private final List expressions;
/**
- * Creates an empty set of consumes request conditions.
+ * Creates a {@link ProducesRequestCondition} with the given producible media type expressions.
+ * @param produces the expressions to parse; if 0 the condition matches to every request
*/
- public ProducesRequestCondition() {
- this(Collections.emptySet());
+ public ProducesRequestCondition(String... produces) {
+ this(produces, null);
+ }
+
+ /**
+ * Creates a {@link ProducesRequestCondition} with the given header and produces expressions.
+ * In addition to produces expressions, {@code "Accept"} header expressions are extracted and treated as
+ * producible media type expressions.
+ * @param produces the produces expressions to parse
+ * @param headers the header expression to parse
+ */
+ public ProducesRequestCondition(String[] produces, String[] headers) {
+ this(parseExpressions(produces, headers));
}
/**
- * Returns a new {@code RequestCondition} that contains all conditions of this key that match the request.
- *
- * @param request the request
- * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match
+ * A private constructor.
+ */
+ private ProducesRequestCondition(Collection expressions) {
+ this.expressions = new ArrayList(expressions);
+ Collections.sort(this.expressions);
+ }
+
+ private static Set parseExpressions(String[] produces, String[] headers) {
+ Set result = new LinkedHashSet();
+ if (headers != null) {
+ for (String header : headers) {
+ HeaderExpression expr = new HeaderExpression(header);
+ if ("Accept".equalsIgnoreCase(expr.name)) {
+ for( MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
+ result.add(new ProduceMediaTypeExpression(mediaType, expr.isNegated));
+ }
+ }
+ }
+ }
+ if (produces != null) {
+ for (String produce : produces) {
+ result.add(new ProduceMediaTypeExpression(produce));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the producible media types contained in all expressions of this condition.
+ */
+ public Set getMediaTypes() {
+ Set result = new LinkedHashSet();
+ for (ProduceMediaTypeExpression expression : expressions) {
+ result.add(expression.getMediaType());
+ }
+ return result;
+ }
+
+ /**
+ * Returns true if this condition contains no producible media type expressions.
+ */
+ public boolean isEmpty() {
+ return expressions.isEmpty();
+ }
+
+ @Override
+ protected Collection getContent() {
+ return expressions;
+ }
+
+ @Override
+ protected boolean isLogicalConjunction() {
+ return false;
+ }
+
+ /**
+ * Returns the "other" instance if "other" as long as it contains any expressions; or "this" instance otherwise.
+ * In other words "other" takes precedence over "this" as long as it contains any expressions.
+ * Example: method-level "produces" overrides type-level "produces" condition.
+ */
+ public ProducesRequestCondition combine(ProducesRequestCondition other) {
+ return !other.expressions.isEmpty() ? other : this;
+ }
+
+ /**
+ * Checks if any of the producible media type expressions match the given request and returns an instance that
+ * is guaranteed to contain matching media type expressions only.
+ *
+ * @param request the current request
+ *
+ * @return the same instance if the condition contains no expressions;
+ * or a new condition with matching expressions; or {@code null} if no expressions match.
*/
public ProducesRequestCondition getMatchingCondition(HttpServletRequest request) {
if (isEmpty()) {
return this;
}
- Set matchingConditions = new LinkedHashSet(getConditions());
- for (Iterator iterator = matchingConditions.iterator(); iterator.hasNext();) {
- ProduceRequestCondition condition = iterator.next();
- if (!condition.match(request)) {
+ Set result = new LinkedHashSet(expressions);
+ for (Iterator iterator = result.iterator(); iterator.hasNext();) {
+ ProduceMediaTypeExpression expression = iterator.next();
+ if (!expression.match(request)) {
iterator.remove();
}
}
- if (matchingConditions.isEmpty()) {
- return null;
- }
- else {
- return new ProducesRequestCondition(matchingConditions);
- }
+ return (result.isEmpty()) ? null : new ProducesRequestCondition(result);
}
/**
- * Combines this collection of request condition with another. Returns {@code other}, unless it has the default
- * value (i.e. {@code */*}).
- *
- * @param other the condition to combine with
+ * Returns:
+ *
+ * - 0 if the two conditions have the same number of expressions
+ *
- Less than 1 if "this" has more in number or more specific producible media type expressions
+ *
- Greater than 1 if "other" has more in number or more specific producible media type expressions
+ *
+ *
+ * It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)}
+ * and each instance contains the matching producible media type expression only or is otherwise empty.
*/
- public ProducesRequestCondition combine(ProducesRequestCondition other) {
- return !other.isEmpty() ? other : this;
- }
-
- public int compareTo(ProducesRequestCondition other, List acceptedMediaTypes) {
+ public int compareTo(ProducesRequestCondition other, HttpServletRequest request) {
+ String acceptHeader = request.getHeader("Accept");
+ List acceptedMediaTypes = MediaType.parseMediaTypes(acceptHeader);
+ MediaType.sortByQualityValue(acceptedMediaTypes);
+
for (MediaType acceptedMediaType : acceptedMediaTypes) {
int thisIndex = this.indexOfMediaType(acceptedMediaType);
int otherIndex = other.indexOfMediaType(acceptedMediaType);
if (thisIndex != otherIndex) {
return otherIndex - thisIndex;
} else if (thisIndex != -1 && otherIndex != -1) {
- ProduceRequestCondition thisCondition = this.getSortedConditions().get(thisIndex);
- ProduceRequestCondition otherCondition = other.getSortedConditions().get(otherIndex);
- int result = thisCondition.compareTo(otherCondition);
+ ProduceMediaTypeExpression thisExpr = this.expressions.get(thisIndex);
+ ProduceMediaTypeExpression otherExpr = other.expressions.get(otherIndex);
+ int result = thisExpr.compareTo(otherExpr);
if (result != 0) {
return result;
}
@@ -118,29 +189,31 @@ public class ProducesRequestCondition
}
private int indexOfMediaType(MediaType mediaType) {
- List sortedConditions = getSortedConditions();
- for (int i = 0; i < sortedConditions.size(); i++) {
- ProduceRequestCondition condition = sortedConditions.get(i);
- if (mediaType.includes(condition.getMediaType())) {
+ for (int i = 0; i < expressions.size(); i++) {
+ if (mediaType.includes(expressions.get(i).getMediaType())) {
return i;
}
}
return -1;
}
- static class ProduceRequestCondition extends MediaTypesRequestCondition.MediaTypeRequestCondition {
+ /**
+ * Parsing and request matching logic for producible media type expressions.
+ * @see RequestMapping#produces()
+ */
+ static class ProduceMediaTypeExpression extends MediaTypeExpression {
- ProduceRequestCondition(MediaType mediaType, boolean negated) {
+ ProduceMediaTypeExpression(MediaType mediaType, boolean negated) {
super(mediaType, negated);
}
- ProduceRequestCondition(String expression) {
+ ProduceMediaTypeExpression(String expression) {
super(expression);
}
@Override
protected boolean match(HttpServletRequest request, MediaType mediaType) {
- List acceptedMediaTypes = getAccept(request);
+ List acceptedMediaTypes = getAcceptedMediaTypes(request);
for (MediaType acceptedMediaType : acceptedMediaTypes) {
if (mediaType.isCompatibleWith(acceptedMediaType)) {
return true;
@@ -149,7 +222,7 @@ public class ProducesRequestCondition
return false;
}
- private List getAccept(HttpServletRequest request) {
+ private List getAcceptedMediaTypes(HttpServletRequest request) {
String acceptHeader = request.getHeader("Accept");
if (StringUtils.hasLength(acceptHeader)) {
return MediaType.parseMediaTypes(acceptHeader);
@@ -159,4 +232,5 @@ public class ProducesRequestCondition
}
}
}
+
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java
index 74619e7c9e6..b8c55727764 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestCondition.java
@@ -19,23 +19,39 @@ package org.springframework.web.servlet.mvc.method.condition;
import javax.servlet.http.HttpServletRequest;
/**
- * Defines the contract for conditions that must be met given an incoming request.
- *
- * Implementations of this interface are created by the {@link RequestConditionFactory}.
- *
+ * The contract for request conditions.
+ *
+ *
Request conditions can be combined (e.g. type + method-level conditions), matched to a request,
+ * or compared to each other to determine if one matches the request better.
+ *
* @author Rossen Stoyanchev
* @author Arjen Poutsma
- * @see RequestConditionFactory
* @since 3.1
*/
-public interface RequestCondition {
+public interface RequestCondition> {
/**
- * Indicates whether this condition matches against the given servlet request.
- *
- * @param request the request
- * @return {@code true} if this condition matches the request; {@code false} otherwise
+ * Defines the rules for combining "this" condition (i.e. the current instance) with another condition.
+ * Example: combine type- and method-level request mapping conditions.
+ *
+ * @returns a request condition instance that is the result of combining the two condition instances.
*/
- boolean match(HttpServletRequest request);
+ This combine(This other);
+ /**
+ * Checks if this condition matches the provided request and returns a potentially new request condition
+ * with content tailored to the current request. For example a condition with URL patterns might return
+ * a new condition that contains matching patterns sorted with best matching patterns on top.
+ *
+ * @return a condition instance in case of a match; or {@code null} if there is no match.
+ */
+ This getMatchingCondition(HttpServletRequest request);
+
+ /**
+ * Compares "this" condition (i.e. the current instance) with another condition in the context of a request.
+ *
Note: it is assumed instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)}
+ * to ensure they have content relevant to current request only.
+ */
+ int compareTo(This other, HttpServletRequest request);
+
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java
deleted file mode 100644
index f67aa1fba31..00000000000
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionFactory.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright 2002-2011 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.web.servlet.mvc.method.condition;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.RequestMethod;
-
-/**
- * Factory for {@link RequestCondition} objects.
- *
- * @author Arjen Poutsma
- * @author Rossen Stoyanchev
- * @since 3.1
- */
-public abstract class RequestConditionFactory {
-
- private static final String CONTENT_TYPE_HEADER = "Content-Type";
-
- private static final String ACCEPT_HEADER = "Accept";
-
- /**
- * Parses the given request methods, and returns them as a single request condition.
- *
- * @param methods the methods
- * @return the request condition
- * @see org.springframework.web.bind.annotation.RequestMapping#method()
- */
- public static RequestMethodsRequestCondition parseMethods(RequestMethod... methods) {
- return methods != null ? new RequestMethodsRequestCondition(methods) : new RequestMethodsRequestCondition();
- }
-
- /**
- * Parses the given parameters, and returns them as a single request condition.
- *
- * @param params the parameters
- * @return the request condition
- * @see org.springframework.web.bind.annotation.RequestMapping#params()
- */
- public static ParamsRequestCondition parseParams(String... params) {
- return params != null ? new ParamsRequestCondition(params) : new ParamsRequestCondition();
- }
-
- /**
- * Parses the given headers, and returns them as a single request condition.
- *
- * @param headers the headers
- * @return the request condition
- * @see org.springframework.web.bind.annotation.RequestMapping#headers()
- */
- public static HeadersRequestCondition parseHeaders(String... headers) {
- if (headers == null) {
- return new HeadersRequestCondition();
- }
- HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers);
-
- // filter out Accept and Content-Type headers, they are dealt with by produces and consumes respectively
- Set filteredConditions =
- new LinkedHashSet(headersCondition.getConditions());
-
- for (Iterator iterator = filteredConditions.iterator();
- iterator.hasNext();) {
- HeadersRequestCondition.HeaderRequestCondition headerCondition = iterator.next();
- if (ACCEPT_HEADER.equalsIgnoreCase(headerCondition.name) ||
- CONTENT_TYPE_HEADER.equalsIgnoreCase(headerCondition.name)) {
- iterator.remove();
- }
- }
- return new HeadersRequestCondition(filteredConditions);
- }
-
- /**
- * Parses the given consumes, and returns them as a single request condition.
- *
- * @param consumes the consumes
- * @return the request condition
- * @see org.springframework.web.bind.annotation.RequestMapping#consumes()
- */
- public static ConsumesRequestCondition parseConsumes(String... consumes) {
- return new ConsumesRequestCondition(consumes);
- }
-
- /**
- * Parses the given consumes and {@code Content-Type} headers, and returns them as a single request condition. Only
- * {@code Content-Type} headers will be used, all other headers will be ignored.
- *
- * @param consumes the consumes
- * @param headers the headers
- * @return the request condition
- * @see org.springframework.web.bind.annotation.RequestMapping#consumes()
- */
- public static ConsumesRequestCondition parseConsumes(String[] consumes, String[] headers) {
-
- List allConditions = parseContentTypeHeaders(headers);
-
- // ignore the default consumes() value if any content-type headers have been set
- boolean headersHasContentType = !allConditions.isEmpty();
- boolean consumesHasDefaultValue = consumes.length == 1 && consumes[0].equals("*/*");
- if (!headersHasContentType || !consumesHasDefaultValue) {
- for (String consume : consumes) {
- allConditions.add(new ConsumesRequestCondition.ConsumeRequestCondition(consume));
- }
- }
- return new ConsumesRequestCondition(allConditions);
- }
-
- private static List parseContentTypeHeaders(String[] headers) {
- List conditions =
- new ArrayList();
- HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers);
- for (HeadersRequestCondition.HeaderRequestCondition headerCondition : headersCondition.getConditions()) {
- if (CONTENT_TYPE_HEADER.equalsIgnoreCase(headerCondition.name)) {
- List mediaTypes = MediaType.parseMediaTypes(headerCondition.value);
- for (MediaType mediaType : mediaTypes) {
- conditions.add(new ConsumesRequestCondition.ConsumeRequestCondition(mediaType,
- headerCondition.isNegated));
- }
- }
- }
- return conditions;
- }
-
- /**
- * Parses the given produces, and returns them as a single request condition.
- *
- * @param produces the produces
- * @return the request condition
- * @see org.springframework.web.bind.annotation.RequestMapping#produces()
- */
- public static ProducesRequestCondition parseProduces(String... produces) {
- return new ProducesRequestCondition(produces);
- }
-
- /**
- * Parses the given produces and {@code Accept} headers, and returns them as a single request condition. Only {@code
- * Accept} headers will be used, all other headers will be ignored.
- *
- * @param produces the consumes
- * @param headers the headers
- * @return the request condition
- * @see org.springframework.web.bind.annotation.RequestMapping#produces()
- */
- public static ProducesRequestCondition parseProduces(String[] produces, String[] headers) {
-
- List allConditions = parseAcceptHeaders(headers);
-
- // ignore the default consumes() value if any accept headers have been set
- boolean headersHasAccept = !allConditions.isEmpty();
- boolean producesHasDefaultValue = produces.length == 1 && produces[0].equals("*/*");
- if (!headersHasAccept || !producesHasDefaultValue) {
- for (String produce : produces) {
- allConditions.add(new ProducesRequestCondition.ProduceRequestCondition(produce));
- }
- }
- return new ProducesRequestCondition(allConditions);
- }
-
- private static List parseAcceptHeaders(String[] headers) {
- List allConditions =
- new ArrayList();
- HeadersRequestCondition headersCondition = new HeadersRequestCondition(headers);
- for (HeadersRequestCondition.HeaderRequestCondition headerCondition : headersCondition.getConditions()) {
- if (ACCEPT_HEADER.equalsIgnoreCase(headerCondition.name)) {
- List mediaTypes = MediaType.parseMediaTypes(headerCondition.value);
- for (MediaType mediaType : mediaTypes) {
- allConditions.add(new ProducesRequestCondition.ProduceRequestCondition(mediaType,
- headerCondition.isNegated));
- }
- }
- }
- return allConditions;
- }
-
-
-}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionSupport.java
similarity index 52%
rename from org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java
rename to org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionSupport.java
index 02914a7725f..7d2faad65aa 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionComposite.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestConditionSupport.java
@@ -17,64 +17,62 @@
package org.springframework.web.servlet.mvc.method.condition;
import java.util.Collection;
-import java.util.Collections;
import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.Set;
/**
- * Abstract base class for {@link RequestCondition} implementations that wrap other request conditions.
- *
- * @author Arjen Poutsma
+ * Base class for {@link RequestCondition}s.
+ *
+ * @author Rossen Stoyanchev
* @since 3.1
*/
-abstract class RequestConditionComposite implements RequestCondition {
-
- private final Set conditions;
-
- protected RequestConditionComposite(Collection conditions) {
- this.conditions = Collections.unmodifiableSet(new LinkedHashSet(conditions));
- }
-
- protected Set getConditions() {
- return conditions;
- }
-
- public boolean isEmpty() {
- return conditions.isEmpty();
- }
-
+abstract class RequestConditionSupport> implements RequestCondition {
+
+ /**
+ * Returns the individual expressions a request condition is composed of such as
+ * URL patterns, HTTP request methods, parameter expressions, etc.
+ */
+ protected abstract Collection> getContent();
+
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o != null && getClass().equals(o.getClass())) {
- RequestConditionComposite other = (RequestConditionComposite) o;
- return this.conditions.equals(other.conditions);
+ RequestConditionSupport> other = (RequestConditionSupport>) o;
+ return getContent().equals(other.getContent());
}
return false;
}
@Override
public int hashCode() {
- return conditions.hashCode();
+ return getContent().hashCode();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("[");
- String infix = getToStringInfix();
- for (Iterator iterator = conditions.iterator(); iterator.hasNext();) {
- RequestCondition condition = iterator.next();
- builder.append(condition.toString());
+ for (Iterator> iterator = getContent().iterator(); iterator.hasNext();) {
+ Object expression = iterator.next();
+ builder.append(expression.toString());
if (iterator.hasNext()) {
- builder.append(infix);
+ if (isLogicalConjunction()) {
+ builder.append(" && ");
+ }
+ else {
+ builder.append(" || ");
+ }
}
}
builder.append("]");
return builder.toString();
}
- protected abstract String getToStringInfix();
+ /**
+ * Returns {@code true} if the individual expressions of the condition are combined via logical
+ * conjunction (" && "); or {@code false} otherwise.
+ */
+ protected abstract boolean isLogicalConjunction();
+
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestCondition.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestCondition.java
index 4a851567b8c..8be1f398b09 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestCondition.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/condition/RequestMethodsRequestCondition.java
@@ -22,132 +22,102 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMethod;
/**
- * Represents a collection of {@link RequestMethod} conditions, typically obtained from {@link
- * org.springframework.web.bind.annotation.RequestMapping#method() @RequestMapping.methods()}.
- *
+ * A logical disjunction (' || ') request condition that matches a request against a set of {@link RequestMethod}s.
+ *
+ * If the condition is created with 0 HTTP request methods, it matches to every request.
+ *
* @author Arjen Poutsma
- * @see RequestConditionFactory#parseMethods(RequestMethod...)
+ * @author Rossen Stoyanchev
* @since 3.1
*/
-public class RequestMethodsRequestCondition
- extends LogicalDisjunctionRequestCondition
- implements Comparable {
-
- private RequestMethodsRequestCondition(Collection conditions) {
- super(conditions);
- }
-
- RequestMethodsRequestCondition(RequestMethod... methods) {
- this(parseConditions(Arrays.asList(methods)));
- }
-
- private static Set parseConditions(List methods) {
- Set conditions =
- new LinkedHashSet(methods.size());
- for (RequestMethod method : methods) {
- conditions.add(new RequestMethodRequestCondition(method));
- }
- return conditions;
- }
+public class RequestMethodsRequestCondition extends RequestConditionSupport {
+ private final Set methods;
/**
- * Creates an empty set of method request conditions.
+ * Create a {@link RequestMethodsRequestCondition} with the given {@link RequestMethod}s.
+ * @param requestMethods 0 or more HTTP request methods; if 0 the condition will match to every request.
*/
- public RequestMethodsRequestCondition() {
- this(Collections.emptySet());
+ public RequestMethodsRequestCondition(RequestMethod... requestMethods) {
+ this(asList(requestMethods));
}
+ private static List asList(RequestMethod... requestMethods) {
+ return requestMethods != null ? Arrays.asList(requestMethods) : Collections.emptyList();
+ }
+
+ /**
+ * Private constructor.
+ */
+ private RequestMethodsRequestCondition(Collection requestMethods) {
+ this.methods = Collections.unmodifiableSet(new LinkedHashSet(requestMethods));
+ }
+
/**
* Returns all {@link RequestMethod}s contained in this condition.
*/
public Set getMethods() {
- Set result = new LinkedHashSet();
- for (RequestMethodRequestCondition condition : getConditions()) {
- result.add(condition.getMethod());
- }
- return result;
+ return methods;
}
- public int compareTo(RequestMethodsRequestCondition other) {
- return other.getConditions().size() - this.getConditions().size();
+ @Override
+ protected Collection getContent() {
+ return methods;
}
+ @Override
+ protected boolean isLogicalConjunction() {
+ return false;
+ }
+
/**
- * Returns a new {@code RequestMethodsRequestCondition} that contains all conditions that match the request.
- *
- * @param request the request
- * @return a new request condition that contains all matching attributes, or {@code null} if not all conditions match
- */
- public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) {
- if (isEmpty()) {
- return this;
- }
- else {
- if (match(request)) {
- return new RequestMethodsRequestCondition(RequestMethod.valueOf(request.getMethod()));
- }
- else {
- return null;
- }
- }
-
- }
-
- /**
- * Combines this collection of request method conditions with another by combining all methods into a logical OR.
- *
- * @param other the condition to combine with
+ * Returns a new instance with a union of the HTTP request methods from "this" and the "other" instance.
*/
public RequestMethodsRequestCondition combine(RequestMethodsRequestCondition other) {
- Set conditions =
- new LinkedHashSet(getConditions());
- conditions.addAll(other.getConditions());
- return new RequestMethodsRequestCondition(conditions);
+ Set set = new LinkedHashSet(this.methods);
+ set.addAll(other.methods);
+ return new RequestMethodsRequestCondition(set);
}
- static class RequestMethodRequestCondition implements RequestCondition {
-
- private final RequestMethod method;
-
- RequestMethodRequestCondition(RequestMethod method) {
- this.method = method;
+ /**
+ * Checks if any of the HTTP request methods match the given request and returns an instance that
+ * contain the matching request method.
+ * @param request the current request
+ * @return the same instance if the condition contains no request method;
+ * or a new condition with the matching request method; or {@code null} if no request methods match.
+ */
+ public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) {
+ if (methods.isEmpty()) {
+ return this;
}
-
- RequestMethod getMethod() {
- return method;
- }
-
- public boolean match(HttpServletRequest request) {
- RequestMethod method = RequestMethod.valueOf(request.getMethod());
- return this.method.equals(method);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
+ RequestMethod incomingRequestMethod = RequestMethod.valueOf(request.getMethod());
+ for (RequestMethod method : methods) {
+ if (method.equals(incomingRequestMethod)) {
+ return new RequestMethodsRequestCondition(method);
}
- if (obj != null && obj instanceof RequestMethodRequestCondition) {
- RequestMethodRequestCondition other = (RequestMethodRequestCondition) obj;
- return this.method.equals(other.method);
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return method.hashCode();
- }
-
- @Override
- public String toString() {
- return method.toString();
}
+ return null;
}
+
+ /**
+ * Returns:
+ *
+ * - 0 if the two conditions contain the same number of HTTP request methods.
+ *
- Less than 1 if "this" instance has an HTTP request method but "other" doesn't.
+ *
- Greater than 1 "other" has an HTTP request method but "this" doesn't.
+ *
+ *
+ * It is assumed that both instances have been obtained via {@link #getMatchingCondition(HttpServletRequest)}
+ * and therefore each instance contains the matching HTTP request method only or is otherwise empty.
+ */
+ public int compareTo(RequestMethodsRequestCondition other, HttpServletRequest request) {
+ return other.methods.size() - this.methods.size();
+ }
+
}
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoComparatorTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoComparatorTests.java
index 01ae7f41a5d..90619c719b5 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoComparatorTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoComparatorTests.java
@@ -16,6 +16,11 @@
package org.springframework.web.servlet.mvc.method;
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
@@ -23,17 +28,14 @@ import java.util.List;
import org.junit.Before;
import org.junit.Test;
-
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
-import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
+import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.RequestMethodsRequestCondition;
import org.springframework.web.util.UrlPathHelper;
-import static java.util.Arrays.*;
-import static org.junit.Assert.*;
-
/**
* Test fixture with {@link RequestMappingHandlerMapping} testing its {@link RequestMappingInfo} comparator.
*
@@ -57,8 +59,8 @@ public class RequestMappingInfoComparatorTests {
request.setRequestURI("/foo");
String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
Comparator comparator = handlerMapping.getMappingComparator(lookupPath, request);
- RequestMappingInfo key1 = new RequestMappingInfo(asList("/fo*"), null);
- RequestMappingInfo key2 = new RequestMappingInfo(asList("/foo"), null);
+ RequestMappingInfo key1 = new RequestMappingInfo(new String[]{"/fo*"});
+ RequestMappingInfo key2 = new RequestMappingInfo(new String[]{"/foo"});
assertEquals(1, comparator.compare(key1, key2));
}
@@ -68,8 +70,8 @@ public class RequestMappingInfoComparatorTests {
request.setRequestURI("/foo");
String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
Comparator comparator = handlerMapping.getMappingComparator(lookupPath, request);
- RequestMappingInfo key1 = new RequestMappingInfo(asList("/foo*"), null);
- RequestMappingInfo key2 = new RequestMappingInfo(asList("/foo*"), null);
+ RequestMappingInfo key1 = new RequestMappingInfo(new String[]{"/foo*"});
+ RequestMappingInfo key2 = new RequestMappingInfo(new String[]{"/foo*"});
assertEquals(0, comparator.compare(key1, key2));
}
@@ -78,20 +80,20 @@ public class RequestMappingInfoComparatorTests {
public void greaterNumberOfMatchingPatternsWins() throws Exception {
request.setRequestURI("/foo.html");
String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
- RequestMappingInfo key1 = new RequestMappingInfo(asList("/foo", "*.jpeg"), null);
- RequestMappingInfo key2 = new RequestMappingInfo(asList("/foo", "*.html"), null);
+ RequestMappingInfo key1 = new RequestMappingInfo(new String[]{"/foo", "*.jpeg"});
+ RequestMappingInfo key2 = new RequestMappingInfo(new String[]{"/foo", "*.html"});
RequestMappingInfo match1 = handlerMapping.getMatchingMapping(key1, lookupPath, request);
RequestMappingInfo match2 = handlerMapping.getMatchingMapping(key2, lookupPath, request);
List matches = asList(match1, match2);
Collections.sort(matches, handlerMapping.getMappingComparator(lookupPath, request));
- assertSame(match2.getPatterns(), matches.get(0).getPatterns());
+ assertSame(match2.getPatternsCondition(), matches.get(0).getPatternsCondition());
}
@Test
public void oneMethodWinsOverNone() {
Comparator comparator = handlerMapping.getMappingComparator("", request);
- RequestMappingInfo key1 = new RequestMappingInfo(null, null);
+ RequestMappingInfo key1 = new RequestMappingInfo(null);
RequestMappingInfo key2 = new RequestMappingInfo(null, new RequestMethod[] {RequestMethod.GET});
assertEquals(1, comparator.compare(key1, key2));
@@ -99,10 +101,11 @@ public class RequestMappingInfoComparatorTests {
@Test
public void methodsAndParams() {
- RequestMappingInfo empty = new RequestMappingInfo(null, null);
+ RequestMappingInfo empty = new RequestMappingInfo(null);
RequestMappingInfo oneMethod = new RequestMappingInfo(null, new RequestMethod[] {RequestMethod.GET});
RequestMappingInfo oneMethodOneParam =
- new RequestMappingInfo(null, RequestConditionFactory.parseMethods(RequestMethod.GET), RequestConditionFactory.parseParams("foo"), null, null, null);
+ new RequestMappingInfo(null, new RequestMethodsRequestCondition(RequestMethod.GET),
+ new ParamsRequestCondition("foo"), null, null, null);
List list = asList(empty, oneMethod, oneMethodOneParam);
Collections.shuffle(list);
Collections.sort(list, handlerMapping.getMappingComparator("", request));
@@ -114,9 +117,9 @@ public class RequestMappingInfoComparatorTests {
@Test
public void produces() {
- RequestMappingInfo html = new RequestMappingInfo(null, null, null, null, null, RequestConditionFactory.parseProduces("text/html"));
- RequestMappingInfo xml = new RequestMappingInfo(null, null, null, null, null, RequestConditionFactory.parseProduces("application/xml"));
- RequestMappingInfo none = new RequestMappingInfo(null, null);
+ RequestMappingInfo html = new RequestMappingInfo(null, null, null, null, null, new ProducesRequestCondition("text/html"));
+ RequestMappingInfo xml = new RequestMappingInfo(null, null, null, null, null, new ProducesRequestCondition("application/xml"));
+ RequestMappingInfo none = new RequestMappingInfo(null);
request.addHeader("Accept", "application/xml, text/html");
Comparator comparator = handlerMapping.getMappingComparator("", request);
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java
index 62861a1f52e..b87f5060bd7 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java
@@ -24,7 +24,6 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import java.lang.reflect.Method;
-import java.util.Arrays;
import java.util.Map;
import org.junit.Before;
@@ -42,9 +41,13 @@ import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.handler.MappedInterceptor;
-import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
-import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
+import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.PatternsRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.RequestMethodsRequestCondition;
import org.springframework.web.util.UrlPathHelper;
/**
@@ -129,7 +132,7 @@ public class RequestMappingInfoHandlerMappingTests {
@Test
public void uriTemplateVariables() {
- RequestMappingInfo key = new RequestMappingInfo(Arrays.asList("/{path1}/{path2}"), null);
+ RequestMappingInfo key = new RequestMappingInfo(new String[] {"/{path1}/{path2}"});
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/1/2");
String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
@@ -197,12 +200,13 @@ public class RequestMappingInfoHandlerMappingTests {
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class> handlerType) {
RequestMapping annotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
- return new RequestMappingInfo(Arrays.asList(annotation.value()),
- RequestConditionFactory.parseMethods(annotation.method()),
- RequestConditionFactory.parseParams(annotation.params()),
- RequestConditionFactory.parseHeaders(annotation.headers()),
- RequestConditionFactory.parseConsumes(annotation.consumes(), annotation.headers()),
- RequestConditionFactory.parseProduces(annotation.produces(), annotation.headers()));
+ return new RequestMappingInfo(
+ new PatternsRequestCondition(annotation.value(), getUrlPathHelper(), getPathMatcher()),
+ new RequestMethodsRequestCondition(annotation.method()),
+ new ParamsRequestCondition(annotation.params()),
+ new HeadersRequestCondition(annotation.headers()),
+ new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
+ new ProducesRequestCondition(annotation.produces(), annotation.headers()));
}
}
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java
index f03017af057..d566c887229 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java
@@ -16,20 +16,19 @@
package org.springframework.web.servlet.mvc.method;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.springframework.web.bind.annotation.RequestMethod.GET;
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+
import org.junit.Test;
-
import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.util.AntPathMatcher;
-import org.springframework.util.PathMatcher;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
-import org.springframework.web.servlet.mvc.method.condition.RequestConditionFactory;
-import org.springframework.web.util.UrlPathHelper;
-
-import static java.util.Arrays.*;
-import static java.util.Collections.*;
-import static org.junit.Assert.*;
-import static org.springframework.web.bind.annotation.RequestMethod.*;
+import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.ParamsRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.PatternsRequestCondition;
+import org.springframework.web.servlet.mvc.method.condition.ProducesRequestCondition;
/**
* Test fixture for {@link RequestMappingInfo} tests.
@@ -41,8 +40,8 @@ public class RequestMappingInfoTests {
@Test
public void equals() {
- RequestMappingInfo key1 = new RequestMappingInfo(singleton("/foo"), methods(GET));
- RequestMappingInfo key2 = new RequestMappingInfo(singleton("/foo"), methods(GET));
+ RequestMappingInfo key1 = new RequestMappingInfo(new String[] {"/foo"}, GET);
+ RequestMappingInfo key2 = new RequestMappingInfo(new String[] {"/foo"}, GET);
assertEquals(key1, key2);
assertEquals(key1.hashCode(), key2.hashCode());
@@ -50,8 +49,8 @@ public class RequestMappingInfoTests {
@Test
public void equalsPrependSlash() {
- RequestMappingInfo key1 = new RequestMappingInfo(singleton("/foo"), methods(GET));
- RequestMappingInfo key2 = new RequestMappingInfo(singleton("foo"), methods(GET));
+ RequestMappingInfo key1 = new RequestMappingInfo(new String[] {"/foo"}, GET);
+ RequestMappingInfo key2 = new RequestMappingInfo(new String[] {"foo"}, GET);
assertEquals(key1, key2);
assertEquals(key1.hashCode(), key2.hashCode());
@@ -59,213 +58,190 @@ public class RequestMappingInfoTests {
@Test
public void combinePatterns() {
- AntPathMatcher pathMatcher = new AntPathMatcher();
+ RequestMappingInfo key1 = createFromPatterns("/t1", "/t2");
+ RequestMappingInfo key2 = createFromPatterns("/m1", "/m2");
+ RequestMappingInfo key3 = createFromPatterns("/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2");
+ assertEquals(key3.getPatternsCondition(), key1.combine(key2).getPatternsCondition());
- RequestMappingInfo key1 = createKeyFromPatterns("/t1", "/t2");
- RequestMappingInfo key2 = createKeyFromPatterns("/m1", "/m2");
- RequestMappingInfo key3 = createKeyFromPatterns("/t1/m1", "/t1/m2", "/t2/m1", "/t2/m2");
- assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns());
+ key1 = createFromPatterns("/t1");
+ key2 = createFromPatterns();
+ key3 = createFromPatterns("/t1");
+ assertEquals(key3.getPatternsCondition(), key1.combine(key2).getPatternsCondition());
- key1 = createKeyFromPatterns("/t1");
- key2 = createKeyFromPatterns();
- key3 = createKeyFromPatterns("/t1");
- assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns());
+ key1 = createFromPatterns();
+ key2 = createFromPatterns("/m1");
+ key3 = createFromPatterns("/m1");
+ assertEquals(key3.getPatternsCondition(), key1.combine(key2).getPatternsCondition());
- key1 = createKeyFromPatterns();
- key2 = createKeyFromPatterns("/m1");
- key3 = createKeyFromPatterns("/m1");
- assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns());
+ key1 = createFromPatterns();
+ key2 = createFromPatterns();
+ key3 = createFromPatterns("");
+ assertEquals(key3.getPatternsCondition(), key1.combine(key2).getPatternsCondition());
- key1 = createKeyFromPatterns();
- key2 = createKeyFromPatterns();
- key3 = createKeyFromPatterns("");
- assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns());
-
- key1 = createKeyFromPatterns("/t1");
- key2 = createKeyFromPatterns("");
- key3 = createKeyFromPatterns("/t1");
- assertEquals(key3.getPatterns(), key1.combine(key2, pathMatcher).getPatterns());
+ key1 = createFromPatterns("/t1");
+ key2 = createFromPatterns("");
+ key3 = createFromPatterns("/t1");
+ assertEquals(key3.getPatternsCondition(), key1.combine(key2).getPatternsCondition());
}
@Test
public void matchPatternsToRequest() {
- UrlPathHelper pathHelper = new UrlPathHelper();
- PathMatcher pathMatcher = new AntPathMatcher();
-
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
- RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null);
- RequestMappingInfo match =
- key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher);
+ RequestMappingInfo match = createFromPatterns("/foo").getMatchingRequestMapping(request);
assertNotNull(match);
request = new MockHttpServletRequest("GET", "/foo/bar");
- key = new RequestMappingInfo(singleton("/foo/*"), null);
- match = key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher);
+ match = createFromPatterns("/foo/*").getMatchingRequestMapping(request);
assertNotNull("Pattern match", match);
request = new MockHttpServletRequest("GET", "/foo.html");
- key = new RequestMappingInfo(singleton("/foo"), null);
- match = key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher);
+ match = createFromPatterns("/foo").getMatchingRequestMapping(request);
assertNotNull("Implicit match by extension", match);
- assertEquals("Contains matched pattern", "/foo.*", match.getPatterns().iterator().next());
+ assertEquals("Contains matched pattern", "/foo.*", match.getPatternsCondition().getPatterns().iterator().next());
request = new MockHttpServletRequest("GET", "/foo/");
- key = new RequestMappingInfo(singleton("/foo"), null);
- match = key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher);
+ match = createFromPatterns("/foo").getMatchingRequestMapping(request);
assertNotNull("Implicit match by trailing slash", match);
- assertEquals("Contains matched pattern", "/foo/", match.getPatterns().iterator().next());
+ assertEquals("Contains matched pattern", "/foo/", match.getPatternsCondition().getPatterns().iterator().next());
request = new MockHttpServletRequest("GET", "/foo.html");
- key = new RequestMappingInfo(singleton("/foo.jpg"), null);
- match = key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher);
+ match = createFromPatterns("/foo.jpg").getMatchingRequestMapping(request);
assertNull("Implicit match ignored if pattern has extension", match);
request = new MockHttpServletRequest("GET", "/foo.html");
- key = new RequestMappingInfo(singleton("/foo.jpg"), null);
- match = key.getMatchingRequestMapping(pathHelper.getLookupPathForRequest(request), request, pathMatcher);
+ match = createFromPatterns("/foo.jpg").getMatchingRequestMapping(request);
assertNull("Implicit match ignored on pattern with trailing slash", match);
}
@Test
public void matchRequestMethods() {
- PathMatcher pathMatcher = new AntPathMatcher();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
- String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
- RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null);
- RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
+ RequestMappingInfo key = createFromPatterns("/foo");
+ RequestMappingInfo match = createFromPatterns("/foo").getMatchingRequestMapping(request);
assertNotNull("No method matches any method", match);
- key = new RequestMappingInfo(singleton("/foo"), methods(GET));
- match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
+ key = new RequestMappingInfo(new String[]{"/foo"}, GET);
+ match = key.getMatchingRequestMapping(request);
assertNotNull("Exact match", match);
- key = new RequestMappingInfo(singleton("/foo"), methods(POST));
- match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
+ key = new RequestMappingInfo(new String[]{"/foo"}, POST);
+ match = key.getMatchingRequestMapping(request);
assertNull("No match", match);
}
@Test
public void matchingKeyContent() {
- PathMatcher pathMatcher = new AntPathMatcher();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
- String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
- RequestMappingInfo key = new RequestMappingInfo(asList("/foo*", "/bar"), methods(GET, POST));
- RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
- RequestMappingInfo expected = new RequestMappingInfo(singleton("/foo*"), methods(GET));
+ RequestMappingInfo key = new RequestMappingInfo(new String[] {"/foo*", "/bar"}, GET, POST);
+ RequestMappingInfo match = key.getMatchingRequestMapping(request);
+ RequestMappingInfo expected = new RequestMappingInfo(new String[] {"/foo*"}, GET);
assertEquals("Matching RequestKey contains matched patterns and methods only", expected, match);
- key = new RequestMappingInfo(asList("/**", "/foo*", "/foo"), null);
- match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
- expected = new RequestMappingInfo(asList("/foo", "/foo*", "/**"), null);
+ key = createFromPatterns("/**", "/foo*", "/foo");
+ match = key.getMatchingRequestMapping(request);
+ expected = createFromPatterns("/foo", "/foo*", "/**");
assertEquals("Matched patterns are sorted with best match at the top", expected, match);
}
@Test
public void paramsCondition() {
- PathMatcher pathMatcher = new AntPathMatcher();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.setParameter("foo", "bar");
- String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
RequestMappingInfo key =
- new RequestMappingInfo(asList("/foo"), null, RequestConditionFactory.parseParams("foo=bar"), null,
- null, null);
- RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
+ new RequestMappingInfo(
+ new PatternsRequestCondition("/foo"), null,
+ new ParamsRequestCondition("foo=bar"), null, null, null);
+ RequestMappingInfo match = key.getMatchingRequestMapping(request);
assertNotNull(match);
- key = new RequestMappingInfo(singleton("/foo"), null, RequestConditionFactory.parseParams("foo!=bar"), null,
- null, null);
- match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
+ key = new RequestMappingInfo(
+ new PatternsRequestCondition("/foo"), null,
+ new ParamsRequestCondition("foo!=bar"), null, null, null);
+ match = key.getMatchingRequestMapping(request);
assertNull(match);
}
@Test
public void headersCondition() {
- PathMatcher pathMatcher = new AntPathMatcher();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.addHeader("foo", "bar");
- String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
RequestMappingInfo key =
- new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo=bar"),
- null, null);
- RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
+ new RequestMappingInfo(
+ new PatternsRequestCondition("/foo"), null, null,
+ new HeadersRequestCondition("foo=bar"), null, null);
+ RequestMappingInfo match = key.getMatchingRequestMapping(request);
assertNotNull(match);
- key = new RequestMappingInfo(singleton("/foo"), null, null, RequestConditionFactory.parseHeaders("foo!=bar"),
- null, null);
- match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
+ key = new RequestMappingInfo(
+ new PatternsRequestCondition("/foo"), null, null,
+ new HeadersRequestCondition("foo!=bar"), null, null);
+ match = key.getMatchingRequestMapping(request);
assertNull(match);
}
@Test
public void consumesCondition() {
- PathMatcher pathMatcher = new AntPathMatcher();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.setContentType("text/plain");
- String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
- RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null, null, null,
- RequestConditionFactory.parseConsumes("text/plain"), null);
- RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
+ RequestMappingInfo key =
+ new RequestMappingInfo(
+ new PatternsRequestCondition("/foo"), null, null, null,
+ new ConsumesRequestCondition("text/plain"), null);
+ RequestMappingInfo match = key.getMatchingRequestMapping(request);
assertNotNull(match);
- key = new RequestMappingInfo(singleton("/foo"), null, null, null,
- RequestConditionFactory.parseConsumes("application/xml"), null);
- match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
+ key = new RequestMappingInfo(
+ new PatternsRequestCondition("/foo"), null, null, null,
+ new ConsumesRequestCondition("application/xml"), null);
+ match = key.getMatchingRequestMapping(request);
assertNull(match);
}
@Test
public void producesCondition() {
- PathMatcher pathMatcher = new AntPathMatcher();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.addHeader("Accept", "text/plain");
- String lookupPath = new UrlPathHelper().getLookupPathForRequest(request);
- RequestMappingInfo key = new RequestMappingInfo(singleton("/foo"), null, null, null,
- null, RequestConditionFactory.parseProduces("text/plain"));
- RequestMappingInfo match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
+ RequestMappingInfo key =
+ new RequestMappingInfo(
+ new PatternsRequestCondition("/foo"), null, null, null, null,
+ new ProducesRequestCondition("text/plain"));
+ RequestMappingInfo match = key.getMatchingRequestMapping(request);
assertNotNull(match);
- key = new RequestMappingInfo(singleton("/foo"), null, null, null, null,
- RequestConditionFactory.parseProduces("application/xml"));
- match = key.getMatchingRequestMapping(lookupPath, request, pathMatcher);
+ key = new RequestMappingInfo(
+ new PatternsRequestCondition("/foo"), null, null, null, null,
+ new ProducesRequestCondition("application/xml"));
+ match = key.getMatchingRequestMapping(request);
assertNull(match);
}
- private RequestMappingInfo createKeyFromPatterns(String... patterns) {
- return new RequestMappingInfo(asList(patterns), null);
- }
-
- private RequestMethod[] methods(RequestMethod... methods) {
- if (methods != null) {
- return methods;
- }
- else {
- return new RequestMethod[0];
- }
+ private RequestMappingInfo createFromPatterns(String... patterns) {
+ return new RequestMappingInfo(patterns);
}
}
\ No newline at end of file
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java
index c5bb910fc59..d93b0d03eb8 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/ConsumesRequestConditionTests.java
@@ -16,13 +16,17 @@
package org.springframework.web.servlet.mvc.method.condition;
-import java.util.Set;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Collection;
import org.junit.Test;
-
import org.springframework.mock.web.MockHttpServletRequest;
-
-import static org.junit.Assert.*;
+import org.springframework.web.servlet.mvc.method.condition.ConsumesRequestCondition.ConsumeMediaTypeExpression;
/**
* @author Arjen Poutsma
@@ -31,75 +35,79 @@ public class ConsumesRequestConditionTests {
@Test
public void consumesMatch() {
- RequestCondition condition = new ConsumesRequestCondition("text/plain");
+ ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text/plain");
- assertTrue(condition.match(request));
+ assertNotNull(condition.getMatchingCondition(request));
}
@Test
public void negatedConsumesMatch() {
- RequestCondition condition = new ConsumesRequestCondition("!text/plain");
+ ConsumesRequestCondition condition = new ConsumesRequestCondition("!text/plain");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text/plain");
- assertFalse(condition.match(request));
+ assertNull(condition.getMatchingCondition(request));
}
@Test
public void consumesWildcardMatch() {
- RequestCondition condition = new ConsumesRequestCondition("text/*");
+ ConsumesRequestCondition condition = new ConsumesRequestCondition("text/*");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text/plain");
- assertTrue(condition.match(request));
+ assertNotNull(condition.getMatchingCondition(request));
}
@Test
public void consumesMultipleMatch() {
- RequestCondition condition = new ConsumesRequestCondition("text/plain", "application/xml");
+ ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain", "application/xml");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text/plain");
- assertTrue(condition.match(request));
+ assertNotNull(condition.getMatchingCondition(request));
}
@Test
public void consumesSingleNoMatch() {
- RequestCondition condition = new ConsumesRequestCondition("text/plain");
+ ConsumesRequestCondition condition = new ConsumesRequestCondition("text/plain");
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("application/xml");
- assertFalse(condition.match(request));
+ assertNull(condition.getMatchingCondition(request));
}
@Test
public void compareToSingle() {
+ MockHttpServletRequest request = new MockHttpServletRequest();
+
ConsumesRequestCondition condition1 = new ConsumesRequestCondition("text/plain");
ConsumesRequestCondition condition2 = new ConsumesRequestCondition("text/*");
- int result = condition1.compareTo(condition2);
+ int result = condition1.compareTo(condition2, request);
assertTrue("Invalid comparison result: " + result, result < 0);
- result = condition2.compareTo(condition1);
+ result = condition2.compareTo(condition1, request);
assertTrue("Invalid comparison result: " + result, result > 0);
}
@Test
public void compareToMultiple() {
+ MockHttpServletRequest request = new MockHttpServletRequest();
+
ConsumesRequestCondition condition1 = new ConsumesRequestCondition("*/*", "text/plain");
ConsumesRequestCondition condition2 = new ConsumesRequestCondition("text/*", "text/plain;q=0.7");
- int result = condition1.compareTo(condition2);
+ int result = condition1.compareTo(condition2, request);
assertTrue("Invalid comparison result: " + result, result < 0);
- result = condition2.compareTo(condition1);
+ result = condition2.compareTo(condition1, request);
assertTrue("Invalid comparison result: " + result, result > 0);
}
@@ -126,28 +134,11 @@ public class ConsumesRequestConditionTests {
public void parseConsumesAndHeaders() {
String[] consumes = new String[] {"text/plain"};
String[] headers = new String[]{"foo=bar", "content-type=application/xml,application/pdf"};
- ConsumesRequestCondition condition = RequestConditionFactory.parseConsumes(consumes, headers);
+ ConsumesRequestCondition condition = new ConsumesRequestCondition(consumes, headers);
assertConditions(condition, "text/plain", "application/xml", "application/pdf");
}
- @Test
- public void parseConsumesDefault() {
- String[] consumes = new String[] {"*/*"};
- String[] headers = new String[0];
- ConsumesRequestCondition condition = RequestConditionFactory.parseConsumes(consumes, headers);
-
- assertConditions(condition, "*/*");
- }
- @Test
- public void parseConsumesDefaultAndHeaders() {
- String[] consumes = new String[] {"*/*"};
- String[] headers = new String[]{"foo=bar", "content-type=text/plain"};
- ConsumesRequestCondition condition = RequestConditionFactory.parseConsumes(consumes, headers);
-
- assertConditions(condition, "text/plain");
- }
-
@Test
public void getMatchingCondition() {
MockHttpServletRequest request = new MockHttpServletRequest();
@@ -165,12 +156,12 @@ public class ConsumesRequestConditionTests {
}
private void assertConditions(ConsumesRequestCondition condition, String... expected) {
- Set conditions = condition.getConditions();
- assertEquals("Invalid amount of conditions", conditions.size(), expected.length);
+ Collection expressions = condition.getContent();
+ assertEquals("Invalid amount of conditions", expressions.size(), expected.length);
for (String s : expected) {
boolean found = false;
- for (ConsumesRequestCondition.ConsumeRequestCondition requestCondition : conditions) {
- String conditionMediaType = requestCondition.getMediaType().toString();
+ for (ConsumeMediaTypeExpression expr : expressions) {
+ String conditionMediaType = expr.getMediaType().toString();
if (conditionMediaType.equals(s)) {
found = true;
break;
@@ -181,12 +172,6 @@ public class ConsumesRequestConditionTests {
fail("Condition [" + s + "] not found");
}
}
-
-
-
}
-
-
-
}
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java
index d480015f19f..092a6310272 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/condition/HeadersRequestConditionTests.java
@@ -16,13 +16,17 @@
package org.springframework.web.servlet.mvc.method.condition;
-import java.util.Set;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
import org.junit.Test;
-
import org.springframework.mock.web.MockHttpServletRequest;
-
-import static org.junit.Assert.*;
+import org.springframework.web.servlet.mvc.method.condition.HeadersRequestCondition.HeaderExpression;
/**
* @author Arjen Poutsma
@@ -40,81 +44,83 @@ public class HeadersRequestConditionTests {
@Test
public void headerPresent() {
- RequestCondition condition = new HeadersRequestCondition("accept");
+ HeadersRequestCondition condition = new HeadersRequestCondition("accept");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Accept", "");
- assertTrue(condition.match(request));
+ assertNotNull(condition.getMatchingCondition(request));
}
@Test
public void headerPresentNoMatch() {
- RequestCondition condition = new HeadersRequestCondition("foo");
+ HeadersRequestCondition condition = new HeadersRequestCondition("foo");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("bar", "");
- assertFalse(condition.match(request));
+ assertNull(condition.getMatchingCondition(request));
}
@Test
public void headerNotPresent() {
- RequestCondition condition = new HeadersRequestCondition("!accept");
+ HeadersRequestCondition condition = new HeadersRequestCondition("!accept");
MockHttpServletRequest request = new MockHttpServletRequest();
- assertTrue(condition.match(request));
+ assertNotNull(condition.getMatchingCondition(request));
}
@Test
public void headerValueMatch() {
- RequestCondition condition = new HeadersRequestCondition("foo=bar");
+ HeadersRequestCondition condition = new HeadersRequestCondition("foo=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bar");
- assertTrue(condition.match(request));
+ assertNotNull(condition.getMatchingCondition(request));
}
@Test
public void headerValueNoMatch() {
- RequestCondition condition = new HeadersRequestCondition("foo=bar");
+ HeadersRequestCondition condition = new HeadersRequestCondition("foo=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bazz");
- assertFalse(condition.match(request));
+ assertNull(condition.getMatchingCondition(request));
}
@Test
public void headerCaseSensitiveValueMatch() {
- RequestCondition condition = new HeadersRequestCondition("foo=Bar");
+ HeadersRequestCondition condition = new HeadersRequestCondition("foo=Bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "bar");
- assertFalse(condition.match(request));
+ assertNull(condition.getMatchingCondition(request));
}
@Test
public void headerValueMatchNegated() {
- RequestCondition condition = new HeadersRequestCondition("foo!=bar");
+ HeadersRequestCondition condition = new HeadersRequestCondition("foo!=bar");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("foo", "baz");
- assertTrue(condition.match(request));
+ assertNotNull(condition.getMatchingCondition(request));
}
@Test
public void compareTo() {
+ MockHttpServletRequest request = new MockHttpServletRequest();
+
HeadersRequestCondition condition1 = new HeadersRequestCondition("foo", "bar", "baz");
HeadersRequestCondition condition2 = new HeadersRequestCondition("foo", "bar");
- int result = condition1.compareTo(condition2);
+ int result = condition1.compareTo(condition2, request);
assertTrue("Invalid comparison result: " + result, result < 0);
- result = condition2.compareTo(condition1);
+ result = condition2.compareTo(condition1, request);
assertTrue("Invalid comparison result: " + result, result > 0);
}
@@ -125,7 +131,7 @@ public class HeadersRequestConditionTests {
HeadersRequestCondition condition2 = new HeadersRequestCondition("foo=baz");
HeadersRequestCondition result = condition1.combine(condition2);
- Set conditions = result.getConditions();
+ Collection