001/* 002 * Copyright 2002-2018 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.Collections; 020import java.util.List; 021import java.util.Map; 022 023import org.springframework.core.MethodParameter; 024import org.springframework.core.ResolvableType; 025import org.springframework.util.CollectionUtils; 026import org.springframework.util.LinkedMultiValueMap; 027import org.springframework.util.MultiValueMap; 028import org.springframework.util.StringUtils; 029import org.springframework.web.bind.annotation.MatrixVariable; 030import org.springframework.web.bind.annotation.ValueConstants; 031import org.springframework.web.bind.support.WebDataBinderFactory; 032import org.springframework.web.context.request.NativeWebRequest; 033import org.springframework.web.context.request.RequestAttributes; 034import org.springframework.web.method.support.HandlerMethodArgumentResolver; 035import org.springframework.web.method.support.ModelAndViewContainer; 036import org.springframework.web.servlet.HandlerMapping; 037 038/** 039 * Resolves arguments of type {@link Map} annotated with {@link MatrixVariable @MatrixVariable} 040 * where the annotation does not specify a name. In other words the purpose of this resolver 041 * is to provide access to multiple matrix variables, either all or associated with a specific 042 * path variable. 043 * 044 * <p>When a name is specified, an argument of type Map is considered to be a single attribute 045 * with a Map value, and is resolved by {@link MatrixVariableMethodArgumentResolver} instead. 046 * 047 * @author Rossen Stoyanchev 048 * @since 3.2 049 */ 050public class MatrixVariableMapMethodArgumentResolver implements HandlerMethodArgumentResolver { 051 052 @Override 053 public boolean supportsParameter(MethodParameter parameter) { 054 MatrixVariable matrixVariable = parameter.getParameterAnnotation(MatrixVariable.class); 055 if (matrixVariable != null) { 056 if (Map.class.isAssignableFrom(parameter.getParameterType())) { 057 return !StringUtils.hasText(matrixVariable.name()); 058 } 059 } 060 return false; 061 } 062 063 @Override 064 public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, 065 NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception { 066 067 @SuppressWarnings("unchecked") 068 Map<String, MultiValueMap<String, String>> matrixVariables = 069 (Map<String, MultiValueMap<String, String>>) request.getAttribute( 070 HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); 071 072 if (CollectionUtils.isEmpty(matrixVariables)) { 073 return Collections.emptyMap(); 074 } 075 076 MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>(); 077 String pathVariable = parameter.getParameterAnnotation(MatrixVariable.class).pathVar(); 078 079 if (!pathVariable.equals(ValueConstants.DEFAULT_NONE)) { 080 MultiValueMap<String, String> mapForPathVariable = matrixVariables.get(pathVariable); 081 if (mapForPathVariable == null) { 082 return Collections.emptyMap(); 083 } 084 map.putAll(mapForPathVariable); 085 } 086 else { 087 for (MultiValueMap<String, String> vars : matrixVariables.values()) { 088 for (String name : vars.keySet()) { 089 for (String value : vars.get(name)) { 090 map.add(name, value); 091 } 092 } 093 } 094 } 095 096 return (isSingleValueMap(parameter) ? map.toSingleValueMap() : map); 097 } 098 099 private boolean isSingleValueMap(MethodParameter parameter) { 100 if (!MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) { 101 ResolvableType[] genericTypes = ResolvableType.forMethodParameter(parameter).getGenerics(); 102 if (genericTypes.length == 2) { 103 return !List.class.isAssignableFrom(genericTypes[1].getRawClass()); 104 } 105 } 106 return false; 107 } 108 109}