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.method;
018
019import java.lang.annotation.Annotation;
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.Collections;
023import java.util.LinkedHashSet;
024import java.util.List;
025import java.util.Set;
026import java.util.function.Predicate;
027
028import org.springframework.core.annotation.AnnotationUtils;
029import org.springframework.util.ClassUtils;
030import org.springframework.util.StringUtils;
031
032/**
033 * A {@code Predicate} to match request handling component types if
034 * <strong>any</strong> of the following selectors match:
035 * <ul>
036 * <li>Base packages -- for selecting handlers by their package.
037 * <li>Assignable types -- for selecting handlers by super type.
038 * <li>Annotations -- for selecting handlers annotated in a specific way.
039 * </ul>
040 * <p>Composability methods on {@link Predicate} can be used :
041 * <pre class="code">
042 * Predicate&lt;Class&lt;?&gt;&gt; predicate =
043 *              HandlerTypePredicate.forAnnotation(RestController.class)
044 *                              .and(HandlerTypePredicate.forBasePackage("org.example"));
045 * </pre>
046 *
047 * @author Rossen Stoyanchev
048 * @since 5.1
049 */
050public final class HandlerTypePredicate implements Predicate<Class<?>> {
051
052        private final Set<String> basePackages;
053
054        private final List<Class<?>> assignableTypes;
055
056        private final List<Class<? extends Annotation>> annotations;
057
058
059        /**
060         * Private constructor. See static factory methods.
061         */
062        private HandlerTypePredicate(Set<String> basePackages, List<Class<?>> assignableTypes,
063                        List<Class<? extends Annotation>> annotations) {
064
065                this.basePackages = Collections.unmodifiableSet(basePackages);
066                this.assignableTypes = Collections.unmodifiableList(assignableTypes);
067                this.annotations = Collections.unmodifiableList(annotations);
068        }
069
070
071        @Override
072        public boolean test(Class<?> controllerType) {
073                if (!hasSelectors()) {
074                        return true;
075                }
076                else if (controllerType != null) {
077                        for (String basePackage : this.basePackages) {
078                                if (controllerType.getName().startsWith(basePackage)) {
079                                        return true;
080                                }
081                        }
082                        for (Class<?> clazz : this.assignableTypes) {
083                                if (ClassUtils.isAssignable(clazz, controllerType)) {
084                                        return true;
085                                }
086                        }
087                        for (Class<? extends Annotation> annotationClass : this.annotations) {
088                                if (AnnotationUtils.findAnnotation(controllerType, annotationClass) != null) {
089                                        return true;
090                                }
091                        }
092                }
093                return false;
094        }
095
096        private boolean hasSelectors() {
097                return (!this.basePackages.isEmpty() || !this.assignableTypes.isEmpty() || !this.annotations.isEmpty());
098        }
099
100
101        // Static factory methods
102
103        /**
104         * {@code Predicate} that applies to any handlers.
105         */
106        public static HandlerTypePredicate forAnyHandlerType() {
107                return new HandlerTypePredicate(
108                                Collections.emptySet(), Collections.emptyList(), Collections.emptyList());
109        }
110
111        /**
112         * Match handlers declared under a base package, e.g. "org.example".
113         * @param packages one or more base package names
114         */
115        public static HandlerTypePredicate forBasePackage(String... packages) {
116                return new Builder().basePackage(packages).build();
117        }
118
119        /**
120         * Type-safe alternative to {@link #forBasePackage(String...)} to specify a
121         * base package through a class.
122         * @param packageClasses one or more base package classes
123         */
124        public static HandlerTypePredicate forBasePackageClass(Class<?>... packageClasses) {
125                return new Builder().basePackageClass(packageClasses).build();
126        }
127
128        /**
129         * Match handlers that are assignable to a given type.
130         * @param types one or more handler super types
131         */
132        public static HandlerTypePredicate forAssignableType(Class<?>... types) {
133                return new Builder().assignableType(types).build();
134        }
135
136        /**
137         * Match handlers annotated with a specific annotation.
138         * @param annotations one or more annotations to check for
139         */
140        @SafeVarargs
141        public static HandlerTypePredicate forAnnotation(Class<? extends Annotation>... annotations) {
142                return new Builder().annotation(annotations).build();
143        }
144
145        /**
146         * Return a builder for a {@code HandlerTypePredicate}.
147         */
148        public static Builder builder() {
149                return new Builder();
150        }
151
152
153        /**
154         * A {@link HandlerTypePredicate} builder.
155         */
156        public static class Builder {
157
158                private final Set<String> basePackages = new LinkedHashSet<>();
159
160                private final List<Class<?>> assignableTypes = new ArrayList<>();
161
162                private final List<Class<? extends Annotation>> annotations = new ArrayList<>();
163
164                /**
165                 * Match handlers declared under a base package, e.g. "org.example".
166                 * @param packages one or more base package classes
167                 */
168                public Builder basePackage(String... packages) {
169                        Arrays.stream(packages).filter(StringUtils::hasText).forEach(this::addBasePackage);
170                        return this;
171                }
172
173                /**
174                 * Type-safe alternative to {@link #forBasePackage(String...)} to specify a
175                 * base package through a class.
176                 * @param packageClasses one or more base package names
177                 */
178                public Builder basePackageClass(Class<?>... packageClasses) {
179                        Arrays.stream(packageClasses).forEach(clazz -> addBasePackage(ClassUtils.getPackageName(clazz)));
180                        return this;
181                }
182
183                private void addBasePackage(String basePackage) {
184                        this.basePackages.add(basePackage.endsWith(".") ? basePackage : basePackage + ".");
185                }
186
187                /**
188                 * Match handlers that are assignable to a given type.
189                 * @param types one or more handler super types
190                 */
191                public Builder assignableType(Class<?>... types) {
192                        this.assignableTypes.addAll(Arrays.asList(types));
193                        return this;
194                }
195
196                /**
197                 * Match types that are annotated with one of the given annotations.
198                 * @param annotations one or more annotations to check for
199                 */
200                @SuppressWarnings("unchecked")
201                public final Builder annotation(Class<? extends Annotation>... annotations) {
202                        this.annotations.addAll(Arrays.asList(annotations));
203                        return this;
204                }
205
206                public HandlerTypePredicate build() {
207                        return new HandlerTypePredicate(this.basePackages, this.assignableTypes, this.annotations);
208                }
209        }
210
211}