/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.autoconfigure.filter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Set;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.ObjectUtils;
/**
* Abstract base class for a {@link TypeExcludeFilter} that can be customized using an
* annotation.
*
* @author Phillip Webb
* @since 1.4.0
*/
public abstract class AnnotationCustomizableTypeExcludeFilter extends TypeExcludeFilter
implements BeanClassLoaderAware {
private ClassLoader classLoader;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
if (hasAnnotation()) {
return !(include(metadataReader, metadataReaderFactory)
&& !exclude(metadataReader, metadataReaderFactory));
}
return false;
}
protected boolean include(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
if (new FilterAnnotations(this.classLoader, getFilters(FilterType.INCLUDE))
.anyMatches(metadataReader, metadataReaderFactory)) {
return true;
}
if (isUseDefaultFilters()
&& defaultInclude(metadataReader, metadataReaderFactory)) {
return true;
}
return false;
}
protected boolean defaultInclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
for (Class<?> include : getDefaultIncludes()) {
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, include)) {
return true;
}
}
for (Class<?> component : getComponentIncludes()) {
if (isTypeOrAnnotated(metadataReader, metadataReaderFactory, component)) {
return true;
}
}
return false;
}
protected boolean exclude(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
return new FilterAnnotations(this.classLoader, getFilters(FilterType.EXCLUDE))
.anyMatches(metadataReader, metadataReaderFactory);
}
@SuppressWarnings("unchecked")
protected final boolean isTypeOrAnnotated(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory, Class<?> type)
throws IOException {
AnnotationTypeFilter annotationFilter = new AnnotationTypeFilter(
(Class<? extends Annotation>) type);
AssignableTypeFilter typeFilter = new AssignableTypeFilter(type);
return annotationFilter.match(metadataReader, metadataReaderFactory)
|| typeFilter.match(metadataReader, metadataReaderFactory);
}
protected abstract boolean hasAnnotation();
protected abstract Filter[] getFilters(FilterType type);
protected abstract boolean isUseDefaultFilters();
protected abstract Set<Class<?>> getDefaultIncludes();
protected abstract Set<Class<?>> getComponentIncludes();
protected enum FilterType {
INCLUDE, EXCLUDE
}
@Override
public int hashCode() {
final int prime = 31;
int result = 0;
result = prime * result + Boolean.hashCode(hasAnnotation());
for (FilterType filterType : FilterType.values()) {
result = prime * result
+ ObjectUtils.nullSafeHashCode(getFilters(filterType));
}
result = prime * result + Boolean.hashCode(isUseDefaultFilters());
result = prime * result + ObjectUtils.nullSafeHashCode(getDefaultIncludes());
result = prime * result + ObjectUtils.nullSafeHashCode(getComponentIncludes());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
AnnotationCustomizableTypeExcludeFilter other = (AnnotationCustomizableTypeExcludeFilter) obj;
boolean result = true;
result = result && hasAnnotation() == other.hasAnnotation();
for (FilterType filterType : FilterType.values()) {
result &= ObjectUtils.nullSafeEquals(getFilters(filterType),
other.getFilters(filterType));
}
result = result && isUseDefaultFilters() == other.isUseDefaultFilters();
result = result && ObjectUtils.nullSafeEquals(getDefaultIncludes(),
other.getDefaultIncludes());
result = result && ObjectUtils.nullSafeEquals(getComponentIncludes(),
other.getComponentIncludes());
return result;
}
}