001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.mvc.method.annotation; 018 019import java.util.ArrayList; 020import java.util.List; 021import java.util.Map; 022 023import org.springframework.core.MethodParameter; 024import org.springframework.util.CollectionUtils; 025import org.springframework.util.MultiValueMap; 026import org.springframework.util.StringUtils; 027import org.springframework.web.bind.ServletRequestBindingException; 028import org.springframework.web.bind.annotation.MatrixVariable; 029import org.springframework.web.bind.annotation.ValueConstants; 030import org.springframework.web.context.request.NativeWebRequest; 031import org.springframework.web.context.request.RequestAttributes; 032import org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver; 033import org.springframework.web.servlet.HandlerMapping; 034 035/** 036 * Resolves method arguments annotated with {@link MatrixVariable @MatrixVariable}. 037 * 038 * <p>If the method parameter is of type Map and no name is specified, then it will 039 * by resolved by the {@link MatrixVariableMapMethodArgumentResolver} instead. 040 * 041 * @author Rossen Stoyanchev 042 * @author Sam Brannen 043 * @since 3.2 044 */ 045public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { 046 047 public MatrixVariableMethodArgumentResolver() { 048 super(null); 049 } 050 051 052 @Override 053 public boolean supportsParameter(MethodParameter parameter) { 054 if (!parameter.hasParameterAnnotation(MatrixVariable.class)) { 055 return false; 056 } 057 if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { 058 String variableName = parameter.getParameterAnnotation(MatrixVariable.class).name(); 059 return StringUtils.hasText(variableName); 060 } 061 return true; 062 } 063 064 @Override 065 protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { 066 MatrixVariable annotation = parameter.getParameterAnnotation(MatrixVariable.class); 067 return new MatrixVariableNamedValueInfo(annotation); 068 } 069 070 @Override 071 @SuppressWarnings("unchecked") 072 protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { 073 Map<String, MultiValueMap<String, String>> pathParameters = (Map<String, MultiValueMap<String, String>>) 074 request.getAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); 075 if (CollectionUtils.isEmpty(pathParameters)) { 076 return null; 077 } 078 079 String pathVar = parameter.getParameterAnnotation(MatrixVariable.class).pathVar(); 080 List<String> paramValues = null; 081 082 if (!pathVar.equals(ValueConstants.DEFAULT_NONE)) { 083 if (pathParameters.containsKey(pathVar)) { 084 paramValues = pathParameters.get(pathVar).get(name); 085 } 086 } 087 else { 088 boolean found = false; 089 paramValues = new ArrayList<String>(); 090 for (MultiValueMap<String, String> params : pathParameters.values()) { 091 if (params.containsKey(name)) { 092 if (found) { 093 String paramType = parameter.getNestedParameterType().getName(); 094 throw new ServletRequestBindingException( 095 "Found more than one match for URI path parameter '" + name + 096 "' for parameter type [" + paramType + "]. Use 'pathVar' attribute to disambiguate."); 097 } 098 paramValues.addAll(params.get(name)); 099 found = true; 100 } 101 } 102 } 103 104 if (CollectionUtils.isEmpty(paramValues)) { 105 return null; 106 } 107 else if (paramValues.size() == 1) { 108 return paramValues.get(0); 109 } 110 else { 111 return paramValues; 112 } 113 } 114 115 @Override 116 protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException { 117 throw new ServletRequestBindingException("Missing matrix variable '" + name + 118 "' for method parameter of type " + parameter.getNestedParameterType().getSimpleName()); 119 } 120 121 122 private static class MatrixVariableNamedValueInfo extends NamedValueInfo { 123 124 private MatrixVariableNamedValueInfo(MatrixVariable annotation) { 125 super(annotation.name(), annotation.required(), annotation.defaultValue()); 126 } 127 } 128 129}