001/*
002 * Copyright 2012-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 *      http://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.boot.test.autoconfigure.filter;
018
019import java.io.IOException;
020import java.lang.annotation.Annotation;
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.Iterator;
024import java.util.List;
025import java.util.regex.Pattern;
026
027import org.springframework.beans.BeanUtils;
028import org.springframework.context.annotation.ComponentScan.Filter;
029import org.springframework.context.annotation.FilterType;
030import org.springframework.core.type.classreading.MetadataReader;
031import org.springframework.core.type.classreading.MetadataReaderFactory;
032import org.springframework.core.type.filter.AnnotationTypeFilter;
033import org.springframework.core.type.filter.AspectJTypeFilter;
034import org.springframework.core.type.filter.AssignableTypeFilter;
035import org.springframework.core.type.filter.RegexPatternTypeFilter;
036import org.springframework.core.type.filter.TypeFilter;
037import org.springframework.util.Assert;
038
039/**
040 * Utility to load {@link TypeFilter TypeFilters} from {@link Filter @Filter} annotations.
041 *
042 * @author Phillip Webb
043 * @since 1.4.0
044 */
045public class FilterAnnotations implements Iterable<TypeFilter> {
046
047        private final ClassLoader classLoader;
048
049        private final List<TypeFilter> filters;
050
051        public FilterAnnotations(ClassLoader classLoader, Filter[] filters) {
052                Assert.notNull(filters, "Filters must not be null");
053                this.classLoader = classLoader;
054                this.filters = createTypeFilters(filters);
055        }
056
057        private List<TypeFilter> createTypeFilters(Filter[] filters) {
058                List<TypeFilter> typeFilters = new ArrayList<>();
059                for (Filter filter : filters) {
060                        for (Class<?> filterClass : filter.classes()) {
061                                typeFilters.add(createTypeFilter(filter.type(), filterClass));
062                        }
063                        for (String pattern : filter.pattern()) {
064                                typeFilters.add(createTypeFilter(filter.type(), pattern));
065                        }
066                }
067                return Collections.unmodifiableList(typeFilters);
068        }
069
070        @SuppressWarnings("unchecked")
071        private TypeFilter createTypeFilter(FilterType filterType, Class<?> filterClass) {
072                switch (filterType) {
073                case ANNOTATION:
074                        Assert.isAssignable(Annotation.class, filterClass,
075                                        "An error occurred while processing an ANNOTATION type filter: ");
076                        return new AnnotationTypeFilter((Class<Annotation>) filterClass);
077                case ASSIGNABLE_TYPE:
078                        return new AssignableTypeFilter(filterClass);
079                case CUSTOM:
080                        Assert.isAssignable(TypeFilter.class, filterClass,
081                                        "An error occurred while processing a CUSTOM type filter: ");
082                        return BeanUtils.instantiateClass(filterClass, TypeFilter.class);
083                }
084                throw new IllegalArgumentException(
085                                "Filter type not supported with Class value: " + filterType);
086        }
087
088        private TypeFilter createTypeFilter(FilterType filterType, String pattern) {
089                switch (filterType) {
090                case ASPECTJ:
091                        return new AspectJTypeFilter(pattern, this.classLoader);
092                case REGEX:
093                        return new RegexPatternTypeFilter(Pattern.compile(pattern));
094                }
095                throw new IllegalArgumentException(
096                                "Filter type not supported with String pattern: " + filterType);
097        }
098
099        @Override
100        public Iterator<TypeFilter> iterator() {
101                return this.filters.iterator();
102        }
103
104        public boolean anyMatches(MetadataReader metadataReader,
105                        MetadataReaderFactory metadataReaderFactory) throws IOException {
106                for (TypeFilter filter : this) {
107                        if (filter.match(metadataReader, metadataReaderFactory)) {
108                                return true;
109                        }
110                }
111                return false;
112        }
113
114}