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.Set;
022
023import org.springframework.beans.factory.BeanClassLoaderAware;
024import org.springframework.boot.context.TypeExcludeFilter;
025import org.springframework.context.annotation.ComponentScan.Filter;
026import org.springframework.core.type.classreading.MetadataReader;
027import org.springframework.core.type.classreading.MetadataReaderFactory;
028import org.springframework.core.type.filter.AnnotationTypeFilter;
029import org.springframework.core.type.filter.AssignableTypeFilter;
030import org.springframework.util.ObjectUtils;
031
032/**
033 * Abstract base class for a {@link TypeExcludeFilter} that can be customized using an
034 * annotation.
035 *
036 * @author Phillip Webb
037 * @since 1.4.0
038 */
039public abstract class AnnotationCustomizableTypeExcludeFilter extends TypeExcludeFilter
040                implements BeanClassLoaderAware {
041
042        private ClassLoader classLoader;
043
044        @Override
045        public void setBeanClassLoader(ClassLoader classLoader) {
046                this.classLoader = classLoader;
047        }
048
049        @Override
050        public boolean match(MetadataReader metadataReader,
051                        MetadataReaderFactory metadataReaderFactory) throws IOException {
052                if (hasAnnotation()) {
053                        return !(include(metadataReader, metadataReaderFactory)
054                                        && !exclude(metadataReader, metadataReaderFactory));
055                }
056                return false;
057        }
058
059        protected boolean include(MetadataReader metadataReader,
060                        MetadataReaderFactory metadataReaderFactory) throws IOException {
061                if (new FilterAnnotations(this.classLoader, getFilters(FilterType.INCLUDE))
062                                .anyMatches(metadataReader, metadataReaderFactory)) {
063                        return true;
064                }
065                if (isUseDefaultFilters()
066                                && defaultInclude(metadataReader, metadataReaderFactory)) {
067                        return true;
068                }
069                return false;
070        }
071
072        protected boolean defaultInclude(MetadataReader metadataReader,
073                        MetadataReaderFactory metadataReaderFactory) throws IOException {
074                for (Class<?> include : getDefaultIncludes()) {
075                        if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, include)) {
076                                return true;
077                        }
078                }
079                for (Class<?> component : getComponentIncludes()) {
080                        if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, component)) {
081                                return true;
082                        }
083                }
084                return false;
085        }
086
087        protected boolean exclude(MetadataReader metadataReader,
088                        MetadataReaderFactory metadataReaderFactory) throws IOException {
089                return new FilterAnnotations(this.classLoader, getFilters(FilterType.EXCLUDE))
090                                .anyMatches(metadataReader, metadataReaderFactory);
091        }
092
093        @SuppressWarnings("unchecked")
094        protected final boolean isTypeOrAnnotated(MetadataReader metadataReader,
095                        MetadataReaderFactory metadataReaderFactory, Class<?> type)
096                        throws IOException {
097                AnnotationTypeFilter annotationFilter = new AnnotationTypeFilter(
098                                (Class<? extends Annotation>) type);
099                AssignableTypeFilter typeFilter = new AssignableTypeFilter(type);
100                return annotationFilter.match(metadataReader, metadataReaderFactory)
101                                || typeFilter.match(metadataReader, metadataReaderFactory);
102        }
103
104        protected abstract boolean hasAnnotation();
105
106        protected abstract Filter[] getFilters(FilterType type);
107
108        protected abstract boolean isUseDefaultFilters();
109
110        protected abstract Set<Class<?>> getDefaultIncludes();
111
112        protected abstract Set<Class<?>> getComponentIncludes();
113
114        protected enum FilterType {
115
116                INCLUDE, EXCLUDE
117
118        }
119
120        @Override
121        public boolean equals(Object obj) {
122                if (this == obj) {
123                        return true;
124                }
125                if (getClass() != obj.getClass()) {
126                        return false;
127                }
128                AnnotationCustomizableTypeExcludeFilter other = (AnnotationCustomizableTypeExcludeFilter) obj;
129                boolean result = true;
130                result = result && hasAnnotation() == other.hasAnnotation();
131                for (FilterType filterType : FilterType.values()) {
132                        result &= ObjectUtils.nullSafeEquals(getFilters(filterType),
133                                        other.getFilters(filterType));
134                }
135                result = result && isUseDefaultFilters() == other.isUseDefaultFilters();
136                result = result && ObjectUtils.nullSafeEquals(getDefaultIncludes(),
137                                other.getDefaultIncludes());
138                result = result && ObjectUtils.nullSafeEquals(getComponentIncludes(),
139                                other.getComponentIncludes());
140                return result;
141        }
142
143        @Override
144        public int hashCode() {
145                final int prime = 31;
146                int result = 0;
147                result = prime * result + Boolean.hashCode(hasAnnotation());
148                for (FilterType filterType : FilterType.values()) {
149                        result = prime * result
150                                        + ObjectUtils.nullSafeHashCode(getFilters(filterType));
151                }
152                result = prime * result + Boolean.hashCode(isUseDefaultFilters());
153                result = prime * result + ObjectUtils.nullSafeHashCode(getDefaultIncludes());
154                result = prime * result + ObjectUtils.nullSafeHashCode(getComponentIncludes());
155                return result;
156        }
157
158}