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}