@ -18,14 +18,15 @@ package org.springframework.boot.autoconfigureprocessor;
@@ -18,14 +18,15 @@ package org.springframework.boot.autoconfigureprocessor;
import java.io.IOException ;
import java.io.OutputStream ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Collections ;
import java.util.HashSet ;
import java.util.LinkedHashMap ;
import java.util.List ;
import java.util.Map ;
import java.util.Map.Entry ;
import java.util.Properties ;
import java.util.Set ;
import java.util.stream.Collectors ;
import java.util.stream.Stream ;
import javax.annotation.processing.AbstractProcessor ;
@ -36,10 +37,8 @@ import javax.lang.model.element.AnnotationMirror;
@@ -36,10 +37,8 @@ import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue ;
import javax.lang.model.element.Element ;
import javax.lang.model.element.ElementKind ;
import javax.lang.model.element.ExecutableElement ;
import javax.lang.model.element.TypeElement ;
import javax.lang.model.type.DeclaredType ;
import javax.lang.model.type.TypeMirror ;
import javax.tools.FileObject ;
import javax.tools.StandardLocation ;
@ -52,6 +51,9 @@ import javax.tools.StandardLocation;
@@ -52,6 +51,9 @@ import javax.tools.StandardLocation;
* /
@SupportedAnnotationTypes ( { "org.springframework.context.annotation.Configuration" ,
"org.springframework.boot.autoconfigure.condition.ConditionalOnClass" ,
"org.springframework.boot.autoconfigure.condition.ConditionalOnBean" ,
"org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate" ,
"org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication" ,
"org.springframework.boot.autoconfigure.AutoConfigureBefore" ,
"org.springframework.boot.autoconfigure.AutoConfigureAfter" ,
"org.springframework.boot.autoconfigure.AutoConfigureOrder" } )
@ -60,7 +62,9 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
@@ -60,7 +62,9 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
protected static final String PROPERTIES_PATH = "META-INF/"
+ "spring-autoconfigure-metadata.properties" ;
private Map < String , String > annotations ;
private final Map < String , String > annotations ;
private final Map < String , ValueExtractor > valueExtractors ;
private final Properties properties = new Properties ( ) ;
@ -68,6 +72,9 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
@@ -68,6 +72,9 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
Map < String , String > annotations = new LinkedHashMap < > ( ) ;
addAnnotations ( annotations ) ;
this . annotations = Collections . unmodifiableMap ( annotations ) ;
Map < String , ValueExtractor > valueExtractors = new LinkedHashMap < > ( ) ;
addValueExtractors ( valueExtractors ) ;
this . valueExtractors = Collections . unmodifiableMap ( valueExtractors ) ;
}
protected void addAnnotations ( Map < String , String > annotations ) {
@ -75,6 +82,12 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
@@ -75,6 +82,12 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
"org.springframework.context.annotation.Configuration" ) ;
annotations . put ( "ConditionalOnClass" ,
"org.springframework.boot.autoconfigure.condition.ConditionalOnClass" ) ;
annotations . put ( "ConditionalOnBean" ,
"org.springframework.boot.autoconfigure.condition.ConditionalOnBean" ) ;
annotations . put ( "ConditionalOnSingleCandidate" ,
"org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate" ) ;
annotations . put ( "ConditionalOnWebApplication" ,
"org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication" ) ;
annotations . put ( "AutoConfigureBefore" ,
"org.springframework.boot.autoconfigure.AutoConfigureBefore" ) ;
annotations . put ( "AutoConfigureAfter" ,
@ -83,6 +96,18 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
@@ -83,6 +96,18 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
"org.springframework.boot.autoconfigure.AutoConfigureOrder" ) ;
}
private void addValueExtractors ( Map < String , ValueExtractor > attributes ) {
attributes . put ( "Configuration" , ValueExtractor . allFrom ( "value" ) ) ;
attributes . put ( "ConditionalOnClass" , ValueExtractor . allFrom ( "value" , "name" ) ) ;
attributes . put ( "ConditionalOnBean" , new OnBeanConditionValueExtractor ( ) ) ;
attributes . put ( "ConditionalOnSingleCandidate" ,
new OnBeanConditionValueExtractor ( ) ) ;
attributes . put ( "ConditionalOnWebApplication" , ValueExtractor . allFrom ( "type" ) ) ;
attributes . put ( "AutoConfigureBefore" , ValueExtractor . allFrom ( "value" , "name" ) ) ;
attributes . put ( "AutoConfigureAfter" , ValueExtractor . allFrom ( "value" , "name" ) ) ;
attributes . put ( "AutoConfigureOrder" , ValueExtractor . allFrom ( "value" ) ) ;
}
@Override
public SourceVersion getSupportedSourceVersion ( ) {
return SourceVersion . latestSupported ( ) ;
@ -123,10 +148,10 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
@@ -123,10 +148,10 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
private void processElement ( Element element , String propertyKey ,
String annotationName ) {
try {
String qualifiedName = getQualifiedName ( element ) ;
String qualifiedName = Elements . getQualifiedName ( element ) ;
AnnotationMirror annotation = getAnnotation ( element , annotationName ) ;
if ( qualifiedName ! = null & & annotation ! = null ) {
List < Object > values = getValues ( annotation ) ;
List < Object > values = getValues ( propertyKey , annotation ) ;
this . properties . put ( qualifiedName + "." + propertyKey ,
toCommaDelimitedString ( values ) ) ;
this . properties . put ( qualifiedName , "" ) ;
@ -158,68 +183,89 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
@@ -158,68 +183,89 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
return result . toString ( ) ;
}
private List < Object > getValues ( AnnotationMirror annotation ) {
return annotation . getElementValues ( ) . entrySet ( ) . stream ( )
. filter ( this : : isNameOrValueAttribute ) . flatMap ( this : : getValues )
. collect ( Collectors . toList ( ) ) ;
}
private boolean isNameOrValueAttribute ( Entry < ? extends ExecutableElement , ? > entry ) {
String attributeName = entry . getKey ( ) . getSimpleName ( ) . toString ( ) ;
return "name" . equals ( attributeName ) | | "value" . equals ( attributeName ) ;
private List < Object > getValues ( String propertyKey , AnnotationMirror annotation ) {
ValueExtractor extractor = this . valueExtractors . get ( propertyKey ) ;
if ( extractor = = null ) {
return Collections . emptyList ( ) ;
}
return extractor . getValues ( annotation ) ;
}
@SuppressWarnings ( "unchecked" )
private Stream < Object > getValues ( Entry < ? , ? extends AnnotationValue > entry ) {
Object value = entry . getValue ( ) . getValue ( ) ;
if ( value instanceof List ) {
return ( ( List < AnnotationValue > ) value ) . stream ( )
. map ( ( annotation ) - > processValue ( annotation . getValue ( ) ) ) ;
private void writeProperties ( ) throws IOException {
if ( ! this . properties . isEmpty ( ) ) {
FileObject file = this . processingEnv . getFiler ( )
. createResource ( StandardLocation . CLASS_OUTPUT , "" , PROPERTIES_PATH ) ;
try ( OutputStream outputStream = file . openOutputStream ( ) ) {
this . properties . store ( outputStream , null ) ;
}
}
return Stream . of ( processValue ( value ) ) ;
}
private Object processValue ( Object value ) {
if ( value instanceof DeclaredType ) {
return getQualifiedName ( ( ( DeclaredType ) value ) . asElement ( ) ) ;
@FunctionalInterface
private interface ValueExtractor {
List < Object > getValues ( AnnotationMirror annotation ) ;
static ValueExtractor allFrom ( String . . . attributes ) {
Set < String > names = new HashSet < > ( Arrays . asList ( attributes ) ) ;
return new AbstractValueExtractor ( ) {
@Override
public List < Object > getValues ( AnnotationMirror annotation ) {
List < Object > result = new ArrayList < > ( ) ;
annotation . getElementValues ( ) . forEach ( ( key , value ) - > {
if ( names . contains ( key . getSimpleName ( ) . toString ( ) ) ) {
extractValues ( value ) . forEach ( result : : add ) ;
}
} ) ;
return result ;
}
} ;
}
return value ;
}
private String getQualifiedName ( Element element ) {
if ( element ! = null ) {
TypeElement enclosingElement = getEnclosingTypeElement ( element . asType ( ) ) ;
if ( enclosingElement ! = null ) {
return getQualifiedName ( enclosingElement ) + "$"
+ ( ( DeclaredType ) element . asType ( ) ) . asElement ( ) . getSimpleName ( )
. toString ( ) ;
private abstract static class AbstractValueExtractor implements ValueExtractor {
@SuppressWarnings ( "unchecked" )
protected Stream < Object > extractValues ( AnnotationValue annotationValue ) {
if ( annotationValue = = null ) {
return Stream . empty ( ) ;
}
if ( element instanceof TypeElement ) {
return ( ( TypeElement ) element ) . getQualifiedName ( ) . toString ( ) ;
Object value = annotationValue . getValue ( ) ;
if ( value instanceof List ) {
return ( ( List < AnnotationValue > ) value ) . stream ( )
. map ( ( annotation ) - > extractValue ( annotation . getValue ( ) ) ) ;
}
return Stream . of ( extractValue ( value ) ) ;
}
return null ;
}
private TypeElement getEnclosingTypeElement ( TypeMirror type ) {
if ( type instanceof DeclaredType ) {
DeclaredType declaredType = ( DeclaredType ) type ;
Element enclosingElement = declaredType . asElement ( ) . getEnclosingElement ( ) ;
if ( enclosingElement ! = null & & enclosingElement instanceof TypeElement ) {
return ( TypeElement ) enclosingElement ;
private Object extractValue ( Object value ) {
if ( value instanceof DeclaredType ) {
return Elements . getQualifiedName ( ( ( DeclaredType ) value ) . asElement ( ) ) ;
}
return value ;
}
return null ;
}
private void writeProperties ( ) throws IOException {
if ( ! this . properties . isEmpty ( ) ) {
FileObject file = this . processingEnv . getFiler ( )
. createResource ( StandardLocation . CLASS_OUTPUT , "" , PROPERTIES_PATH ) ;
try ( OutputStream outputStream = file . openOutputStream ( ) ) {
this . properties . store ( outputStream , null ) ;
private static class OnBeanConditionValueExtractor extends AbstractValueExtractor {
@Override
public List < Object > getValues ( AnnotationMirror annotation ) {
Map < String , AnnotationValue > attributes = new LinkedHashMap < > ( ) ;
annotation . getElementValues ( ) . forEach ( ( key , value ) - > attributes
. put ( key . getSimpleName ( ) . toString ( ) , value ) ) ;
if ( attributes . containsKey ( "name" ) ) {
return Collections . emptyList ( ) ;
}
List < Object > result = new ArrayList < > ( ) ;
extractValues ( attributes . get ( "value" ) ) . forEach ( result : : add ) ;
extractValues ( attributes . get ( "type" ) ) . forEach ( result : : add ) ;
return result ;
}
}
}