View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.web.servlet.mvc.method.annotation;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.springframework.core.MethodParameter;
24  import org.springframework.util.CollectionUtils;
25  import org.springframework.util.MultiValueMap;
26  import org.springframework.util.StringUtils;
27  import org.springframework.web.bind.ServletRequestBindingException;
28  import org.springframework.web.bind.annotation.MatrixVariable;
29  import org.springframework.web.bind.annotation.ValueConstants;
30  import org.springframework.web.context.request.NativeWebRequest;
31  import org.springframework.web.context.request.RequestAttributes;
32  import org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver;
33  import org.springframework.web.servlet.HandlerMapping;
34  
35  /**
36   * Resolves method arguments annotated with an {@link MatrixVariable @PathParam}.
37   *
38   * <p>If the method parameter is of type Map and no name is specified, then it will
39   * by resolved by the {@link MatrixVariableMapMethodArgumentResolver} instead.
40   *
41   * @author Rossen Stoyanchev
42   * @since 3.2
43   */
44  public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
45  
46  	public MatrixVariableMethodArgumentResolver() {
47  		super(null);
48  	}
49  
50  	@Override
51  	public boolean supportsParameter(MethodParameter parameter) {
52  		if (!parameter.hasParameterAnnotation(MatrixVariable.class)) {
53  			return false;
54  		}
55  		if (Map.class.isAssignableFrom(parameter.getParameterType())) {
56  			String paramName = parameter.getParameterAnnotation(MatrixVariable.class).value();
57  			return StringUtils.hasText(paramName);
58  		}
59  		return true;
60  	}
61  
62  	@Override
63  	protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
64  		MatrixVariable annotation = parameter.getParameterAnnotation(MatrixVariable.class);
65  		return new PathParamNamedValueInfo(annotation);
66  	}
67  
68  	@Override
69  	protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
70  
71  		@SuppressWarnings("unchecked")
72  		Map<String, MultiValueMap<String, String>> pathParameters =
73  			(Map<String, MultiValueMap<String, String>>) request.getAttribute(
74  					HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
75  
76  		if (CollectionUtils.isEmpty(pathParameters)) {
77  			return null;
78  		}
79  
80  		String pathVar = parameter.getParameterAnnotation(MatrixVariable.class).pathVar();
81  		List<String> paramValues = null;
82  
83  		if (!pathVar.equals(ValueConstants.DEFAULT_NONE)) {
84  			if (pathParameters.containsKey(pathVar)) {
85  				paramValues = pathParameters.get(pathVar).get(name);
86  			}
87  		}
88  		else {
89  			boolean found = false;
90  			paramValues = new ArrayList<String>();
91  			for (MultiValueMap<String, String> params : pathParameters.values()) {
92  				if (params.containsKey(name)) {
93  					if (found) {
94  						String paramType = parameter.getParameterType().getName();
95  						throw new ServletRequestBindingException(
96  								"Found more than one match for URI path parameter '" + name +
97  								"' for parameter type [" + paramType + "]. Use pathVar attribute to disambiguate.");
98  					}
99  					paramValues.addAll(params.get(name));
100 					found = true;
101 				}
102 			}
103 		}
104 
105 		if (CollectionUtils.isEmpty(paramValues)) {
106 			return null;
107 		}
108 		else if (paramValues.size() == 1) {
109 			return paramValues.get(0);
110 		}
111 		else {
112 			return paramValues;
113 		}
114 	}
115 
116 	@Override
117 	protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
118 		throw new ServletRequestBindingException("Missing matrix variable '" + name +
119 				"' for method parameter of type " + parameter.getParameterType().getSimpleName());
120 	}
121 
122 
123 	private static class PathParamNamedValueInfo extends NamedValueInfo {
124 
125 		private PathParamNamedValueInfo(MatrixVariable annotation) {
126 			super(annotation.value(), annotation.required(), annotation.defaultValue());
127 		}
128 	}
129 }