001/*
002 * Copyright 2002-2015 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.method.support;
018
019import java.util.Collection;
020import java.util.Collections;
021import java.util.LinkedList;
022import java.util.List;
023import java.util.Map;
024
025import org.springframework.core.MethodParameter;
026import org.springframework.core.convert.ConversionService;
027import org.springframework.format.support.DefaultFormattingConversionService;
028import org.springframework.util.Assert;
029import org.springframework.web.util.UriComponentsBuilder;
030
031/**
032 * A {@link UriComponentsContributor} containing a list of other contributors
033 * to delegate and also encapsulating a specific {@link ConversionService} to
034 * use for formatting method argument values to Strings.
035 *
036 * @author Rossen Stoyanchev
037 * @since 4.0
038 */
039public class CompositeUriComponentsContributor implements UriComponentsContributor {
040
041        private final List<Object> contributors = new LinkedList<Object>();
042
043        private final ConversionService conversionService;
044
045
046        /**
047         * Create an instance from a collection of {@link UriComponentsContributor}s or
048         * {@link HandlerMethodArgumentResolver}s. Since both of these tend to be implemented
049         * by the same class, the most convenient option is to obtain the configured
050         * {@code HandlerMethodArgumentResolvers} in {@code RequestMappingHandlerAdapter}
051         * and provide that to this constructor.
052         * @param contributors a collection of {@link UriComponentsContributor}
053         * or {@link HandlerMethodArgumentResolver}s.
054         */
055        public CompositeUriComponentsContributor(UriComponentsContributor... contributors) {
056                Collections.addAll(this.contributors, contributors);
057                this.conversionService = new DefaultFormattingConversionService();
058        }
059
060        /**
061         * Create an instance from a collection of {@link UriComponentsContributor}s or
062         * {@link HandlerMethodArgumentResolver}s. Since both of these tend to be implemented
063         * by the same class, the most convenient option is to obtain the configured
064         * {@code HandlerMethodArgumentResolvers} in {@code RequestMappingHandlerAdapter}
065         * and provide that to this constructor.
066         * @param contributors a collection of {@link UriComponentsContributor}
067         * or {@link HandlerMethodArgumentResolver}s.
068         */
069        public CompositeUriComponentsContributor(Collection<?> contributors) {
070                this(contributors, null);
071        }
072
073        /**
074         * Create an instance from a collection of {@link UriComponentsContributor}s or
075         * {@link HandlerMethodArgumentResolver}s. Since both of these tend to be implemented
076         * by the same class, the most convenient option is to obtain the configured
077         * {@code HandlerMethodArgumentResolvers} in the {@code RequestMappingHandlerAdapter}
078         * and provide that to this constructor.
079         * <p>If the {@link ConversionService} argument is {@code null},
080         * {@link org.springframework.format.support.DefaultFormattingConversionService}
081         * will be used by default.
082         * @param contributors a collection of {@link UriComponentsContributor}
083         * or {@link HandlerMethodArgumentResolver}s.
084         * @param cs a ConversionService to use when method argument values
085         * need to be formatted as Strings before being added to the URI
086         */
087        public CompositeUriComponentsContributor(Collection<?> contributors, ConversionService cs) {
088                Assert.notNull(contributors, "'uriComponentsContributors' must not be null");
089                this.contributors.addAll(contributors);
090                this.conversionService = (cs != null ? cs : new DefaultFormattingConversionService());
091        }
092
093
094        public boolean hasContributors() {
095                return this.contributors.isEmpty();
096        }
097
098        @Override
099        public boolean supportsParameter(MethodParameter parameter) {
100                for (Object c : this.contributors) {
101                        if (c instanceof UriComponentsContributor) {
102                                UriComponentsContributor contributor = (UriComponentsContributor) c;
103                                if (contributor.supportsParameter(parameter)) {
104                                        return true;
105                                }
106                        }
107                        else if (c instanceof HandlerMethodArgumentResolver) {
108                                if (((HandlerMethodArgumentResolver) c).supportsParameter(parameter)) {
109                                        return false;
110                                }
111                        }
112                }
113                return false;
114        }
115
116        @Override
117        public void contributeMethodArgument(MethodParameter parameter, Object value,
118                        UriComponentsBuilder builder, Map<String, Object> uriVariables, ConversionService conversionService) {
119
120                for (Object contributor : this.contributors) {
121                        if (contributor instanceof UriComponentsContributor) {
122                                UriComponentsContributor ucc = (UriComponentsContributor) contributor;
123                                if (ucc.supportsParameter(parameter)) {
124                                        ucc.contributeMethodArgument(parameter, value, builder, uriVariables, conversionService);
125                                        break;
126                                }
127                        }
128                        else if (contributor instanceof HandlerMethodArgumentResolver) {
129                                if (((HandlerMethodArgumentResolver) contributor).supportsParameter(parameter)) {
130                                        break;
131                                }
132                        }
133                }
134        }
135
136        /**
137         * An overloaded method that uses the ConversionService created at construction.
138         */
139        public void contributeMethodArgument(MethodParameter parameter, Object value, UriComponentsBuilder builder,
140                        Map<String, Object> uriVariables) {
141
142                this.contributeMethodArgument(parameter, value, builder, uriVariables, this.conversionService);
143        }
144
145}