001/*
002 * Copyright 2002-2020 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;
018
019import java.util.List;
020import java.util.Set;
021
022import javax.servlet.http.HttpServletRequest;
023
024import org.springframework.http.HttpMethod;
025import org.springframework.lang.Nullable;
026import org.springframework.util.ObjectUtils;
027import org.springframework.util.PathMatcher;
028import org.springframework.util.StringUtils;
029import org.springframework.web.accept.ContentNegotiationManager;
030import org.springframework.web.bind.annotation.RequestMethod;
031import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
032import org.springframework.web.servlet.mvc.condition.HeadersRequestCondition;
033import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
034import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
035import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
036import org.springframework.web.servlet.mvc.condition.RequestCondition;
037import org.springframework.web.servlet.mvc.condition.RequestConditionHolder;
038import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
039import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
040import org.springframework.web.util.UrlPathHelper;
041
042/**
043 * Request mapping information. Encapsulates the following request mapping conditions:
044 * <ol>
045 * <li>{@link PatternsRequestCondition}
046 * <li>{@link RequestMethodsRequestCondition}
047 * <li>{@link ParamsRequestCondition}
048 * <li>{@link HeadersRequestCondition}
049 * <li>{@link ConsumesRequestCondition}
050 * <li>{@link ProducesRequestCondition}
051 * <li>{@code RequestCondition} (optional, custom request condition)
052 * </ol>
053 *
054 * @author Arjen Poutsma
055 * @author Rossen Stoyanchev
056 * @since 3.1
057 */
058public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
059
060        private static final PatternsRequestCondition EMPTY_PATTERNS = new PatternsRequestCondition();
061
062        private static final RequestMethodsRequestCondition EMPTY_REQUEST_METHODS = new RequestMethodsRequestCondition();
063
064        private static final ParamsRequestCondition EMPTY_PARAMS = new ParamsRequestCondition();
065
066        private static final HeadersRequestCondition EMPTY_HEADERS = new HeadersRequestCondition();
067
068        private static final ConsumesRequestCondition EMPTY_CONSUMES = new ConsumesRequestCondition();
069
070        private static final ProducesRequestCondition EMPTY_PRODUCES = new ProducesRequestCondition();
071
072        private static final RequestConditionHolder EMPTY_CUSTOM = new RequestConditionHolder(null);
073
074
075        @Nullable
076        private final String name;
077
078        private final PatternsRequestCondition patternsCondition;
079
080        private final RequestMethodsRequestCondition methodsCondition;
081
082        private final ParamsRequestCondition paramsCondition;
083
084        private final HeadersRequestCondition headersCondition;
085
086        private final ConsumesRequestCondition consumesCondition;
087
088        private final ProducesRequestCondition producesCondition;
089
090        private final RequestConditionHolder customConditionHolder;
091
092        private final int hashCode;
093
094
095        public RequestMappingInfo(@Nullable String name, @Nullable PatternsRequestCondition patterns,
096                        @Nullable RequestMethodsRequestCondition methods, @Nullable ParamsRequestCondition params,
097                        @Nullable HeadersRequestCondition headers, @Nullable ConsumesRequestCondition consumes,
098                        @Nullable ProducesRequestCondition produces, @Nullable RequestCondition<?> custom) {
099
100                this.name = (StringUtils.hasText(name) ? name : null);
101                this.patternsCondition = (patterns != null ? patterns : EMPTY_PATTERNS);
102                this.methodsCondition = (methods != null ? methods : EMPTY_REQUEST_METHODS);
103                this.paramsCondition = (params != null ? params : EMPTY_PARAMS);
104                this.headersCondition = (headers != null ? headers : EMPTY_HEADERS);
105                this.consumesCondition = (consumes != null ? consumes : EMPTY_CONSUMES);
106                this.producesCondition = (produces != null ? produces : EMPTY_PRODUCES);
107                this.customConditionHolder = (custom != null ? new RequestConditionHolder(custom) : EMPTY_CUSTOM);
108
109                this.hashCode = calculateHashCode(
110                                this.patternsCondition, this.methodsCondition, this.paramsCondition, this.headersCondition,
111                                this.consumesCondition, this.producesCondition, this.customConditionHolder);
112        }
113
114        /**
115         * Creates a new instance with the given request conditions.
116         */
117        public RequestMappingInfo(@Nullable PatternsRequestCondition patterns,
118                        @Nullable RequestMethodsRequestCondition methods, @Nullable ParamsRequestCondition params,
119                        @Nullable HeadersRequestCondition headers, @Nullable ConsumesRequestCondition consumes,
120                        @Nullable ProducesRequestCondition produces, @Nullable RequestCondition<?> custom) {
121
122                this(null, patterns, methods, params, headers, consumes, produces, custom);
123        }
124
125        /**
126         * Re-create a RequestMappingInfo with the given custom request condition.
127         */
128        public RequestMappingInfo(RequestMappingInfo info, @Nullable RequestCondition<?> customRequestCondition) {
129                this(info.name, info.patternsCondition, info.methodsCondition, info.paramsCondition, info.headersCondition,
130                                info.consumesCondition, info.producesCondition, customRequestCondition);
131        }
132
133        /**
134         * Return the name for this mapping, or {@code null}.
135         */
136        @Nullable
137        public String getName() {
138                return this.name;
139        }
140
141        /**
142         * Return the URL patterns of this {@link RequestMappingInfo};
143         * or instance with 0 patterns (never {@code null}).
144         */
145        public PatternsRequestCondition getPatternsCondition() {
146                return this.patternsCondition;
147        }
148
149        /**
150         * Return the HTTP request methods of this {@link RequestMappingInfo};
151         * or instance with 0 request methods (never {@code null}).
152         */
153        public RequestMethodsRequestCondition getMethodsCondition() {
154                return this.methodsCondition;
155        }
156
157        /**
158         * Return the "parameters" condition of this {@link RequestMappingInfo};
159         * or instance with 0 parameter expressions (never {@code null}).
160         */
161        public ParamsRequestCondition getParamsCondition() {
162                return this.paramsCondition;
163        }
164
165        /**
166         * Return the "headers" condition of this {@link RequestMappingInfo};
167         * or instance with 0 header expressions (never {@code null}).
168         */
169        public HeadersRequestCondition getHeadersCondition() {
170                return this.headersCondition;
171        }
172
173        /**
174         * Return the "consumes" condition of this {@link RequestMappingInfo};
175         * or instance with 0 consumes expressions (never {@code null}).
176         */
177        public ConsumesRequestCondition getConsumesCondition() {
178                return this.consumesCondition;
179        }
180
181        /**
182         * Return the "produces" condition of this {@link RequestMappingInfo};
183         * or instance with 0 produces expressions (never {@code null}).
184         */
185        public ProducesRequestCondition getProducesCondition() {
186                return this.producesCondition;
187        }
188
189        /**
190         * Return the "custom" condition of this {@link RequestMappingInfo}, or {@code null}.
191         */
192        @Nullable
193        public RequestCondition<?> getCustomCondition() {
194                return this.customConditionHolder.getCondition();
195        }
196
197
198        /**
199         * Combine "this" request mapping info (i.e. the current instance) with another request mapping info instance.
200         * <p>Example: combine type- and method-level request mappings.
201         * @return a new request mapping info instance; never {@code null}
202         */
203        @Override
204        public RequestMappingInfo combine(RequestMappingInfo other) {
205                String name = combineNames(other);
206                PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition);
207                RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
208                ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
209                HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
210                ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
211                ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
212                RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);
213
214                return new RequestMappingInfo(name, patterns,
215                                methods, params, headers, consumes, produces, custom.getCondition());
216        }
217
218        @Nullable
219        private String combineNames(RequestMappingInfo other) {
220                if (this.name != null && other.name != null) {
221                        String separator = RequestMappingInfoHandlerMethodMappingNamingStrategy.SEPARATOR;
222                        return this.name + separator + other.name;
223                }
224                else if (this.name != null) {
225                        return this.name;
226                }
227                else {
228                        return other.name;
229                }
230        }
231
232        /**
233         * Checks if all conditions in this request mapping info match the provided request and returns
234         * a potentially new request mapping info with conditions tailored to the current request.
235         * <p>For example the returned instance may contain the subset of URL patterns that match to
236         * the current request, sorted with best matching patterns on top.
237         * @return a new instance in case all conditions match; or {@code null} otherwise
238         */
239        @Override
240        @Nullable
241        public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
242                RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
243                if (methods == null) {
244                        return null;
245                }
246                ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
247                if (params == null) {
248                        return null;
249                }
250                HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
251                if (headers == null) {
252                        return null;
253                }
254                ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
255                if (consumes == null) {
256                        return null;
257                }
258                ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
259                if (produces == null) {
260                        return null;
261                }
262                PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
263                if (patterns == null) {
264                        return null;
265                }
266                RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
267                if (custom == null) {
268                        return null;
269                }
270
271                return new RequestMappingInfo(this.name, patterns,
272                                methods, params, headers, consumes, produces, custom.getCondition());
273        }
274
275        /**
276         * Compares "this" info (i.e. the current instance) with another info in the context of a request.
277         * <p>Note: It is assumed both instances have been obtained via
278         * {@link #getMatchingCondition(HttpServletRequest)} to ensure they have conditions with
279         * content relevant to current request.
280         */
281        @Override
282        public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
283                int result;
284                // Automatic vs explicit HTTP HEAD mapping
285                if (HttpMethod.HEAD.matches(request.getMethod())) {
286                        result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
287                        if (result != 0) {
288                                return result;
289                        }
290                }
291                result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
292                if (result != 0) {
293                        return result;
294                }
295                result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
296                if (result != 0) {
297                        return result;
298                }
299                result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
300                if (result != 0) {
301                        return result;
302                }
303                result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
304                if (result != 0) {
305                        return result;
306                }
307                result = this.producesCondition.compareTo(other.getProducesCondition(), request);
308                if (result != 0) {
309                        return result;
310                }
311                // Implicit (no method) vs explicit HTTP method mappings
312                result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
313                if (result != 0) {
314                        return result;
315                }
316                result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
317                if (result != 0) {
318                        return result;
319                }
320                return 0;
321        }
322
323        @Override
324        public boolean equals(@Nullable Object other) {
325                if (this == other) {
326                        return true;
327                }
328                if (!(other instanceof RequestMappingInfo)) {
329                        return false;
330                }
331                RequestMappingInfo otherInfo = (RequestMappingInfo) other;
332                return (this.patternsCondition.equals(otherInfo.patternsCondition) &&
333                                this.methodsCondition.equals(otherInfo.methodsCondition) &&
334                                this.paramsCondition.equals(otherInfo.paramsCondition) &&
335                                this.headersCondition.equals(otherInfo.headersCondition) &&
336                                this.consumesCondition.equals(otherInfo.consumesCondition) &&
337                                this.producesCondition.equals(otherInfo.producesCondition) &&
338                                this.customConditionHolder.equals(otherInfo.customConditionHolder));
339        }
340
341        @Override
342        public int hashCode() {
343                return this.hashCode;
344        }
345
346        private static int calculateHashCode(
347                        PatternsRequestCondition patterns, RequestMethodsRequestCondition methods,
348                        ParamsRequestCondition params, HeadersRequestCondition headers,
349                        ConsumesRequestCondition consumes, ProducesRequestCondition produces,
350                        RequestConditionHolder custom) {
351
352                return patterns.hashCode() * 31 + methods.hashCode() + params.hashCode() +
353                                headers.hashCode() + consumes.hashCode() + produces.hashCode() + custom.hashCode();
354        }
355
356        @Override
357        public String toString() {
358                StringBuilder builder = new StringBuilder("{");
359                if (!this.methodsCondition.isEmpty()) {
360                        Set<RequestMethod> httpMethods = this.methodsCondition.getMethods();
361                        builder.append(httpMethods.size() == 1 ? httpMethods.iterator().next() : httpMethods);
362                }
363                if (!this.patternsCondition.isEmpty()) {
364                        Set<String> patterns = this.patternsCondition.getPatterns();
365                        builder.append(" ").append(patterns.size() == 1 ? patterns.iterator().next() : patterns);
366                }
367                if (!this.paramsCondition.isEmpty()) {
368                        builder.append(", params ").append(this.paramsCondition);
369                }
370                if (!this.headersCondition.isEmpty()) {
371                        builder.append(", headers ").append(this.headersCondition);
372                }
373                if (!this.consumesCondition.isEmpty()) {
374                        builder.append(", consumes ").append(this.consumesCondition);
375                }
376                if (!this.producesCondition.isEmpty()) {
377                        builder.append(", produces ").append(this.producesCondition);
378                }
379                if (!this.customConditionHolder.isEmpty()) {
380                        builder.append(", and ").append(this.customConditionHolder);
381                }
382                builder.append('}');
383                return builder.toString();
384        }
385
386
387        /**
388         * Create a new {@code RequestMappingInfo.Builder} with the given paths.
389         * @param paths the paths to use
390         * @since 4.2
391         */
392        public static Builder paths(String... paths) {
393                return new DefaultBuilder(paths);
394        }
395
396
397        /**
398         * Defines a builder for creating a RequestMappingInfo.
399         * @since 4.2
400         */
401        public interface Builder {
402
403                /**
404                 * Set the path patterns.
405                 */
406                Builder paths(String... paths);
407
408                /**
409                 * Set the request method conditions.
410                 */
411                Builder methods(RequestMethod... methods);
412
413                /**
414                 * Set the request param conditions.
415                 */
416                Builder params(String... params);
417
418                /**
419                 * Set the header conditions.
420                 * <p>By default this is not set.
421                 */
422                Builder headers(String... headers);
423
424                /**
425                 * Set the consumes conditions.
426                 */
427                Builder consumes(String... consumes);
428
429                /**
430                 * Set the produces conditions.
431                 */
432                Builder produces(String... produces);
433
434                /**
435                 * Set the mapping name.
436                 */
437                Builder mappingName(String name);
438
439                /**
440                 * Set a custom condition to use.
441                 */
442                Builder customCondition(RequestCondition<?> condition);
443
444                /**
445                 * Provide additional configuration needed for request mapping purposes.
446                 */
447                Builder options(BuilderConfiguration options);
448
449                /**
450                 * Build the RequestMappingInfo.
451                 */
452                RequestMappingInfo build();
453        }
454
455
456        private static class DefaultBuilder implements Builder {
457
458                private String[] paths;
459
460                private RequestMethod[] methods = new RequestMethod[0];
461
462                private String[] params = new String[0];
463
464                private String[] headers = new String[0];
465
466                private String[] consumes = new String[0];
467
468                private String[] produces = new String[0];
469
470                private boolean hasContentType;
471
472                private boolean hasAccept;
473
474                @Nullable
475                private String mappingName;
476
477                @Nullable
478                private RequestCondition<?> customCondition;
479
480                private BuilderConfiguration options = new BuilderConfiguration();
481
482                public DefaultBuilder(String... paths) {
483                        this.paths = paths;
484                }
485
486                @Override
487                public Builder paths(String... paths) {
488                        this.paths = paths;
489                        return this;
490                }
491
492                @Override
493                public DefaultBuilder methods(RequestMethod... methods) {
494                        this.methods = methods;
495                        return this;
496                }
497
498                @Override
499                public DefaultBuilder params(String... params) {
500                        this.params = params;
501                        return this;
502                }
503
504                @Override
505                public DefaultBuilder headers(String... headers) {
506                        for (String header : headers) {
507                                this.hasContentType = this.hasContentType ||
508                                                header.contains("Content-Type") || header.contains("content-type");
509                                this.hasAccept = this.hasAccept ||
510                                                header.contains("Accept") || header.contains("accept");
511                        }
512                        this.headers = headers;
513                        return this;
514                }
515
516                @Override
517                public DefaultBuilder consumes(String... consumes) {
518                        this.consumes = consumes;
519                        return this;
520                }
521
522                @Override
523                public DefaultBuilder produces(String... produces) {
524                        this.produces = produces;
525                        return this;
526                }
527
528                @Override
529                public DefaultBuilder mappingName(String name) {
530                        this.mappingName = name;
531                        return this;
532                }
533
534                @Override
535                public DefaultBuilder customCondition(RequestCondition<?> condition) {
536                        this.customCondition = condition;
537                        return this;
538                }
539
540                @Override
541                public Builder options(BuilderConfiguration options) {
542                        this.options = options;
543                        return this;
544                }
545
546                @Override
547                @SuppressWarnings("deprecation")
548                public RequestMappingInfo build() {
549
550                        PatternsRequestCondition patternsCondition = ObjectUtils.isEmpty(this.paths) ? null :
551                                        new PatternsRequestCondition(
552                                                        this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
553                                                        this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
554                                                        this.options.getFileExtensions());
555
556                        ContentNegotiationManager manager = this.options.getContentNegotiationManager();
557
558                        return new RequestMappingInfo(this.mappingName, patternsCondition,
559                                        ObjectUtils.isEmpty(this.methods) ?
560                                                        null : new RequestMethodsRequestCondition(this.methods),
561                                        ObjectUtils.isEmpty(this.params) ?
562                                                        null : new ParamsRequestCondition(this.params),
563                                        ObjectUtils.isEmpty(this.headers) ?
564                                                        null : new HeadersRequestCondition(this.headers),
565                                        ObjectUtils.isEmpty(this.consumes) && !this.hasContentType ?
566                                                        null : new ConsumesRequestCondition(this.consumes, this.headers),
567                                        ObjectUtils.isEmpty(this.produces) && !this.hasAccept ?
568                                                        null : new ProducesRequestCondition(this.produces, this.headers, manager),
569                                        this.customCondition);
570                }
571        }
572
573
574        /**
575         * Container for configuration options used for request mapping purposes.
576         * Such configuration is required to create RequestMappingInfo instances but
577         * is typically used across all RequestMappingInfo instances.
578         * @since 4.2
579         * @see Builder#options
580         */
581        public static class BuilderConfiguration {
582
583                @Nullable
584                private UrlPathHelper urlPathHelper;
585
586                @Nullable
587                private PathMatcher pathMatcher;
588
589                private boolean trailingSlashMatch = true;
590
591                private boolean suffixPatternMatch = true;
592
593                private boolean registeredSuffixPatternMatch = false;
594
595                @Nullable
596                private ContentNegotiationManager contentNegotiationManager;
597
598                /**
599                 * Set a custom UrlPathHelper to use for the PatternsRequestCondition.
600                 * <p>By default this is not set.
601                 * @since 4.2.8
602                 */
603                public void setUrlPathHelper(@Nullable UrlPathHelper urlPathHelper) {
604                        this.urlPathHelper = urlPathHelper;
605                }
606
607                /**
608                 * Return a custom UrlPathHelper to use for the PatternsRequestCondition, if any.
609                 */
610                @Nullable
611                public UrlPathHelper getUrlPathHelper() {
612                        return this.urlPathHelper;
613                }
614
615                /**
616                 * Set a custom PathMatcher to use for the PatternsRequestCondition.
617                 * <p>By default this is not set.
618                 */
619                public void setPathMatcher(@Nullable PathMatcher pathMatcher) {
620                        this.pathMatcher = pathMatcher;
621                }
622
623                /**
624                 * Return a custom PathMatcher to use for the PatternsRequestCondition, if any.
625                 */
626                @Nullable
627                public PathMatcher getPathMatcher() {
628                        return this.pathMatcher;
629                }
630
631                /**
632                 * Set whether to apply trailing slash matching in PatternsRequestCondition.
633                 * <p>By default this is set to 'true'.
634                 */
635                public void setTrailingSlashMatch(boolean trailingSlashMatch) {
636                        this.trailingSlashMatch = trailingSlashMatch;
637                }
638
639                /**
640                 * Return whether to apply trailing slash matching in PatternsRequestCondition.
641                 */
642                public boolean useTrailingSlashMatch() {
643                        return this.trailingSlashMatch;
644                }
645
646                /**
647                 * Set whether to apply suffix pattern matching in PatternsRequestCondition.
648                 * <p>By default this is set to 'true'.
649                 * @see #setRegisteredSuffixPatternMatch(boolean)
650                 * @deprecated as of 5.2.4. See class-level note in
651                 * {@link RequestMappingHandlerMapping} on the deprecation of path
652                 * extension config options.
653                 */
654                @Deprecated
655                public void setSuffixPatternMatch(boolean suffixPatternMatch) {
656                        this.suffixPatternMatch = suffixPatternMatch;
657                }
658
659                /**
660                 * Return whether to apply suffix pattern matching in PatternsRequestCondition.
661                 * @deprecated as of 5.2.4. See class-level note in
662                 * {@link RequestMappingHandlerMapping} on the deprecation of path
663                 * extension config options.
664                 */
665                @Deprecated
666                public boolean useSuffixPatternMatch() {
667                        return this.suffixPatternMatch;
668                }
669
670                /**
671                 * Set whether suffix pattern matching should be restricted to registered
672                 * file extensions only. Setting this property also sets
673                 * {@code suffixPatternMatch=true} and requires that a
674                 * {@link #setContentNegotiationManager} is also configured in order to
675                 * obtain the registered file extensions.
676                 * @deprecated as of 5.2.4. See class-level note in
677                 * {@link RequestMappingHandlerMapping} on the deprecation of path
678                 * extension config options; note also that in 5.3 the default for this
679                 * property switches from {@code false} to {@code true}.
680                 */
681                @Deprecated
682                public void setRegisteredSuffixPatternMatch(boolean registeredSuffixPatternMatch) {
683                        this.registeredSuffixPatternMatch = registeredSuffixPatternMatch;
684                        this.suffixPatternMatch = (registeredSuffixPatternMatch || this.suffixPatternMatch);
685                }
686
687                /**
688                 * Return whether suffix pattern matching should be restricted to registered
689                 * file extensions only.
690                 * @deprecated as of 5.2.4. See class-level note in
691                 * {@link RequestMappingHandlerMapping} on the deprecation of path
692                 * extension config options.
693                 */
694                @Deprecated
695                public boolean useRegisteredSuffixPatternMatch() {
696                        return this.registeredSuffixPatternMatch;
697                }
698
699                /**
700                 * Return the file extensions to use for suffix pattern matching. If
701                 * {@code registeredSuffixPatternMatch=true}, the extensions are obtained
702                 * from the configured {@code contentNegotiationManager}.
703                 * @deprecated as of 5.2.4. See class-level note in
704                 * {@link RequestMappingHandlerMapping} on the deprecation of path
705                 * extension config options.
706                 */
707                @Nullable
708                @Deprecated
709                public List<String> getFileExtensions() {
710                        if (useRegisteredSuffixPatternMatch() && this.contentNegotiationManager != null) {
711                                return this.contentNegotiationManager.getAllFileExtensions();
712                        }
713                        return null;
714                }
715
716                /**
717                 * Set the ContentNegotiationManager to use for the ProducesRequestCondition.
718                 * <p>By default this is not set.
719                 */
720                public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
721                        this.contentNegotiationManager = contentNegotiationManager;
722                }
723
724                /**
725                 * Return the ContentNegotiationManager to use for the ProducesRequestCondition,
726                 * if any.
727                 */
728                @Nullable
729                public ContentNegotiationManager getContentNegotiationManager() {
730                        return this.contentNegotiationManager;
731                }
732        }
733
734}