getSupportedAnnotationTypes() {
return Collections.singleton(GlideExtension.class.getName());
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/GlideAnnotationProcessor.java
================================================
package com.bumptech.glide.annotation.compiler;
import com.bumptech.glide.annotation.GlideType;
import com.google.auto.service.AutoService;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
// Links in Javadoc will work due to build setup, even though there is no direct dependency here.
/**
* Generates classes based on Glide's annotations that configure Glide, add support for additional
* resource types, and/or extend Glide's API.
*
* This processor discovers all {@code AppGlideModule} and {@code LibraryGlideModule}
* implementations that are annotated with {@link com.bumptech.glide.annotation.GlideModule}. Any
* implementations missing the annotation will be ignored.
*
*
This processor also discovers all {@link com.bumptech.glide.annotation.GlideExtension}
* annotated classes.
*
*
Multiple classes are generated by this processor:
*
*
* For {@code LibraryGlideModule}s - A GlideIndexer class in a specific package that will
* later be used by the processor to discover all {@code LibraryGlideModule} classes.
* For {@code AppGlideModule}s - A single {@code AppGlideModule} implementation ({@code
* com.bumptech.glide.GeneratedAppGlideModule}) that calls all {@code LibraryGlideModule}s and
* the original {@code AppGlideModule} in the correct order when Glide is initialized.
* {@link com.bumptech.glide.annotation.GlideExtension}s -
*
* A {@code com.bumptech.glide.request.RequestOptions} implementation that contains
* static versions of all builder methods in the base class and both static and instance
* versions of methods in all {@link com.bumptech.glide.annotation.GlideExtension}s.
* If one or more methods in one or more {@link
* com.bumptech.glide.annotation.GlideExtension} annotated classes are annotated with
* {@link GlideType}:
*
* A {@code com.bumptech.glide.RequestManager} implementation containing a
* generated method for each method annotated with {@link GlideType}.
* A {@code
* com.bumptech.glide.manager.RequestManagerRetriever.RequestManagerFactory}
* implementation that produces the generated {@code
* com.bumptech.glide.RequestManager}s.
* A {@code com.bumptech.glide.Glide} look-alike that implements all static
* methods in the {@code com.bumptech.glide.Glide} singleton and returns the
* generated {@code com.bumptech.glide.RequestManager} implementation when
* appropriate.
*
*
*
*
* {@code AppGlideModule} implementations must only be included in applications, not in
* libraries. There must be exactly one {@code AppGlideModule} implementation per Application. The
* {@code AppGlideModule} class is used as a signal that all modules have been found and that the
* final merged {@code com.bumptech.glide.GeneratedAppGlideModule} impl can be created.
*/
@AutoService(Processor.class)
public final class GlideAnnotationProcessor extends AbstractProcessor {
static final boolean DEBUG = false;
private ProcessorUtil processorUtil;
private LibraryModuleProcessor libraryModuleProcessor;
private AppModuleProcessor appModuleProcessor;
private boolean isGeneratedAppGlideModuleWritten;
private ExtensionProcessor extensionProcessor;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
processorUtil = new ProcessorUtil(processingEnvironment);
IndexerGenerator indexerGenerator = new IndexerGenerator(processorUtil);
libraryModuleProcessor = new LibraryModuleProcessor(processorUtil, indexerGenerator);
appModuleProcessor = new AppModuleProcessor(processingEnvironment, processorUtil);
extensionProcessor =
new ExtensionProcessor(processingEnvironment, processorUtil, indexerGenerator);
}
@Override
public Set getSupportedAnnotationTypes() {
Set result = new HashSet<>();
result.addAll(libraryModuleProcessor.getSupportedAnnotationTypes());
result.addAll(extensionProcessor.getSupportedAnnotationTypes());
return result;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* Each round we do the following:
*
*
* Find all {@code AppGlideModule}s and save them to an instance variable (throw if > 1).
* Find all {@code LibraryGlideModule}s
* For each {@code LibraryGlideModule}, write an {@code Indexer} with an Annotation with the
* class name.
* If we wrote any {@code Indexer}s, return and wait for the next round.
* If we didn't write any {@code Indexer}s and there is a {@code AppGlideModule}, write the
* {@code GeneratedAppGlideModule}. Once the {@code GeneratedAppGlideModule} is written, we
* expect to be finished. Any further generation of related classes will result in errors.
*
*/
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment env) {
processorUtil.process();
boolean newModulesWritten = libraryModuleProcessor.processModules(env);
boolean newExtensionWritten = extensionProcessor.processExtensions(env);
appModuleProcessor.processModules(set, env);
if (newExtensionWritten || newModulesWritten) {
if (isGeneratedAppGlideModuleWritten) {
throw new IllegalStateException("Cannot process annotations after writing AppGlideModule");
}
return false;
}
if (!isGeneratedAppGlideModuleWritten) {
isGeneratedAppGlideModuleWritten = appModuleProcessor.maybeWriteAppModule();
}
return false;
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/GlideExtensionValidator.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.ProcessorUtil.nonNulls;
import com.bumptech.glide.annotation.GlideOption;
import com.bumptech.glide.annotation.GlideType;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.squareup.javapoet.ClassName;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic.Kind;
/**
* Validates that classes annotated with {@link com.bumptech.glide.annotation.GlideExtension}
* contains methods with the expected format.
*
* Validation is performed so that errors can be found when a library is compiled. Without
* validation, an error written in to a library wouldn't be found until Glide tried to generate code
* for an Application.
*/
final class GlideExtensionValidator {
private final ProcessingEnvironment processingEnvironment;
private final ProcessorUtil processorUtil;
GlideExtensionValidator(
ProcessingEnvironment processingEnvironment, ProcessorUtil processorUtil) {
this.processingEnvironment = processingEnvironment;
this.processorUtil = processorUtil;
}
void validateExtension(TypeElement typeElement) {
if (!typeElement.getModifiers().contains(Modifier.PUBLIC)) {
throw new IllegalArgumentException(
"RequestOptionsExtensions must be public, including: " + getName(typeElement));
}
for (Element element : typeElement.getEnclosedElements()) {
if (element.getKind() == ElementKind.CONSTRUCTOR) {
validateExtensionConstructor(element);
} else if (element.getKind() == ElementKind.METHOD) {
ExecutableElement executableElement = (ExecutableElement) element;
if (executableElement.getAnnotation(GlideOption.class) != null) {
validateGlideOption(executableElement);
} else if (executableElement.getAnnotation(GlideType.class) != null) {
validateGlideType(executableElement);
}
}
}
}
private static String getQualifiedMethodName(ExecutableElement executableElement) {
return getEnclosingClassName(executableElement) + "#" + getName(executableElement);
}
private static String getEnclosingClassName(Element element) {
return element.getEnclosingElement().toString();
}
private static String getName(Element element) {
return element.toString();
}
private static void validateExtensionConstructor(Element element) {
if (!element.getModifiers().contains(Modifier.PRIVATE)) {
throw new IllegalArgumentException(
"RequestOptionsExtensions must be public, with private constructors and only static"
+ " methods. Found a non-private constructor in: "
+ getEnclosingClassName(element));
}
ExecutableElement executableElement = (ExecutableElement) element;
if (!executableElement.getParameters().isEmpty()) {
throw new IllegalArgumentException(
"RequestOptionsExtensions must be public, with private constructors and only static"
+ " methods. Found parameters in the constructor of: "
+ getEnclosingClassName(element));
}
}
private void validateGlideOption(ExecutableElement executableElement) {
validateGlideOptionAnnotations(executableElement);
validateGlideOptionParameters(executableElement);
TypeMirror returnType = executableElement.getReturnType();
if (!isBaseRequestOptions(returnType)) {
throw new IllegalArgumentException(
"@GlideOption methods should return a"
+ " BaseRequestOptions> object, but "
+ getQualifiedMethodName(executableElement)
+ " returns "
+ returnType
+ ". If you're using old style @GlideOption methods, your"
+ " method may have a void return type, but doing so is deprecated and support will"
+ " be removed in a future version");
}
validateGlideOptionOverride(executableElement);
}
private void validateGlideOptionAnnotations(ExecutableElement executableElement) {
validateAnnotatedNonNull(executableElement);
}
private static void validateGlideOptionParameters(ExecutableElement executableElement) {
if (executableElement.getParameters().isEmpty()) {
throw new IllegalArgumentException(
"@GlideOption methods must take a "
+ "BaseRequestOptions> object as their first parameter, but "
+ getQualifiedMethodName(executableElement)
+ " has none");
}
VariableElement first = executableElement.getParameters().get(0);
TypeMirror expected = first.asType();
if (!isBaseRequestOptions(expected)) {
throw new IllegalArgumentException(
"@GlideOption methods must take a"
+ " BaseRequestOptions> object as their first parameter, but the first parameter"
+ " in "
+ getQualifiedMethodName(executableElement)
+ " is "
+ expected);
}
}
private static boolean isBaseRequestOptions(TypeMirror typeMirror) {
return typeMirror.toString().equals("com.bumptech.glide.request.BaseRequestOptions>");
}
private void validateGlideOptionOverride(ExecutableElement element) {
int overrideType = processorUtil.getOverrideType(element);
boolean isOverridingBaseRequestOptionsMethod = isMethodInBaseRequestOptions(element);
if (isOverridingBaseRequestOptionsMethod && overrideType == GlideOption.OVERRIDE_NONE) {
throw new IllegalArgumentException(
"Accidentally attempting to override a method in"
+ " BaseRequestOptions. Add an 'override' value in the @GlideOption annotation"
+ " if this is intentional. Offending method: "
+ getQualifiedMethodName(element));
} else if (!isOverridingBaseRequestOptionsMethod && overrideType != GlideOption.OVERRIDE_NONE) {
throw new IllegalArgumentException(
"Requested to override an existing method in"
+ " BaseRequestOptions, but no such method was found. Offending method: "
+ getQualifiedMethodName(element));
}
}
private boolean isMethodInBaseRequestOptions(ExecutableElement toFind) {
// toFind is a method in a GlideExtension whose first argument is a BaseRequestOptions> type.
// Since we're comparing against methods in BaseRequestOptions itself, we need to drop that
// first type.
TypeElement requestOptionsType =
processingEnvironment
.getElementUtils()
.getTypeElement(RequestOptionsGenerator.BASE_REQUEST_OPTIONS_QUALIFIED_NAME);
List toFindParameterNames = getComparableParameterNames(toFind, true /*skipFirst*/);
String toFindSimpleName = toFind.getSimpleName().toString();
for (Element element : requestOptionsType.getEnclosedElements()) {
if (element.getKind() != ElementKind.METHOD) {
continue;
}
ExecutableElement inBase = (ExecutableElement) element;
if (toFindSimpleName.equals(inBase.getSimpleName().toString())) {
List parameterNamesInBase =
getComparableParameterNames(inBase, false /*skipFirst*/);
if (parameterNamesInBase.equals(toFindParameterNames)) {
return true;
}
}
}
return false;
}
private static List getComparableParameterNames(
ExecutableElement element, boolean skipFirst) {
List extends VariableElement> parameters = element.getParameters();
if (skipFirst) {
parameters = parameters.subList(1, parameters.size());
}
List result = new ArrayList<>(parameters.size());
for (VariableElement parameter : parameters) {
result.add(parameter.asType().toString());
}
return result;
}
private void validateGlideType(ExecutableElement executableElement) {
TypeMirror returnType = executableElement.getReturnType();
validateGlideTypeAnnotations(executableElement);
if (!isRequestBuilder(returnType) || !typeMatchesExpected(returnType, executableElement)) {
String expectedClassName = getGlideTypeValue(executableElement);
throw new IllegalArgumentException(
"@GlideType methods should return a RequestBuilder<"
+ expectedClassName
+ "> object, but "
+ getQualifiedMethodName(executableElement)
+ " returns: "
+ returnType
+ ". If you're using old style @GlideType methods, your"
+ " method may have a void return type, but doing so is deprecated and support will"
+ " be removed in a future version");
}
validateGlideTypeParameters(executableElement);
}
private String getGlideTypeValue(ExecutableElement executableElement) {
return processorUtil
.findClassValuesFromAnnotationOnClassAsNames(executableElement, GlideType.class)
.iterator()
.next();
}
private boolean typeMatchesExpected(TypeMirror returnType, ExecutableElement executableElement) {
if (!(returnType instanceof DeclaredType)) {
return false;
}
List extends TypeMirror> typeArguments = ((DeclaredType) returnType).getTypeArguments();
if (typeArguments.size() != 1) {
return false;
}
TypeMirror argument = typeArguments.get(0);
String expected = getGlideTypeValue(executableElement);
return argument.toString().equals(expected);
}
private boolean isRequestBuilder(TypeMirror typeMirror) {
TypeMirror toCompare = processingEnvironment.getTypeUtils().erasure(typeMirror);
return toCompare.toString().equals("com.bumptech.glide.RequestBuilder");
}
private static void validateGlideTypeParameters(ExecutableElement executableElement) {
if (executableElement.getParameters().size() != 1) {
throw new IllegalArgumentException(
"@GlideType methods must take a"
+ " RequestBuilder object as their first and only parameter, but given multiple for: "
+ getQualifiedMethodName(executableElement));
}
VariableElement first = executableElement.getParameters().get(0);
TypeMirror argumentType = first.asType();
if (!argumentType.toString().startsWith("com.bumptech.glide.RequestBuilder")) {
throw new IllegalArgumentException(
"@GlideType methods must take a"
+ " RequestBuilder object as their first and only parameter, but given: "
+ argumentType
+ " for: "
+ getQualifiedMethodName(executableElement));
}
}
private void validateGlideTypeAnnotations(ExecutableElement executableElement) {
validateAnnotatedNonNull(executableElement);
}
private void validateAnnotatedNonNull(ExecutableElement executableElement) {
Set annotationNames =
FluentIterable.from(executableElement.getAnnotationMirrors())
.transform(
new Function() {
@Override
public String apply(AnnotationMirror input) {
return input.getAnnotationType().asElement().toString();
}
})
.toSet();
boolean noNonNull = true;
for (ClassName nonNull : nonNulls()) {
if (annotationNames.contains(nonNull.reflectionName())) {
noNonNull = false;
break;
}
}
if (noNonNull) {
processingEnvironment
.getMessager()
.printMessage(
Kind.WARNING,
getQualifiedMethodName(executableElement)
+ " is missing the "
+ processorUtil.nonNull().reflectionName()
+ " annotation,"
+ " please add it to ensure that your extension methods are always returning"
+ " non-null values");
}
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/GlideGenerator.java
================================================
package com.bumptech.glide.annotation.compiler;
import com.bumptech.glide.annotation.GlideExtension;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.MethodSpec.Builder;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeSpec;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
/**
* Generates a Glide look-alike that acts as the entry point to the generated API
* (GlideApp.with(...)).
*
* Generated {@code com.bumptech.glide.Glide} look-alikes look like this (note that the name is
* configurable in {@link com.bumptech.glide.annotation.GlideModule}):
*
*
*
* public final class GlideApp {
* private GiphyGlide() {
* }
*
* public static File getPhotoCacheDir(Context context) {
* return Glide.getPhotoCacheDir(context);
* }
*
* public static File getPhotoCacheDir(Context context, String cacheName) {
* return Glide.getPhotoCacheDir(context, cacheName);
* }
*
* public static Glide get(Context context) {
* return Glide.get(context);
* }
*
* public static void tearDown() {
* Glide.tearDown();
* }
*
* public static GeneratedRequestManager with(Context context) {
* return (GeneratedRequestManager) Glide.with(context);
* }
*
* public static GeneratedRequestManager with(Activity activity) {
* return (GeneratedRequestManager) Glide.with(activity);
* }
*
* public static GeneratedRequestManager with(FragmentActivity activity) {
* return (GeneratedRequestManager) Glide.with(activity);
* }
*
* public static GeneratedRequestManager with(Fragment fragment) {
* return (GeneratedRequestManager) Glide.with(fragment);
* }
*
* public static GeneratedRequestManager with(androidx.fragment.app.Fragment fragment) {
* return (GeneratedRequestManager) Glide.with(fragment);
* }
*
*
*/
final class GlideGenerator {
private static final String GLIDE_QUALIFIED_NAME = "com.bumptech.glide.Glide";
private static final String REQUEST_MANAGER_QUALIFIED_NAME = "com.bumptech.glide.RequestManager";
private static final String SUPPRESS_LINT_PACKAGE_NAME = "android.annotation";
private static final String SUPPRESS_LINT_CLASS_NAME = "SuppressLint";
private final ProcessingEnvironment processingEnv;
private final ProcessorUtil processorUtil;
private final TypeElement glideType;
private final TypeElement requestManagerType;
GlideGenerator(ProcessingEnvironment processingEnv, ProcessorUtil processorUtil) {
this.processingEnv = processingEnv;
this.processorUtil = processorUtil;
Elements elementUtils = processingEnv.getElementUtils();
requestManagerType = elementUtils.getTypeElement(REQUEST_MANAGER_QUALIFIED_NAME);
glideType = elementUtils.getTypeElement(GLIDE_QUALIFIED_NAME);
}
TypeSpec generate(
String generatedCodePackageName, String glideName, TypeSpec generatedRequestManager) {
return TypeSpec.classBuilder(glideName)
.addJavadoc(
"The entry point for interacting with Glide for Applications\n"
+ "\n"
+ "Includes all generated APIs from all\n"
+ "{@link $T}s in source and dependent libraries.\n"
+ "\n"
+ "
This class is generated and should not be modified"
+ "\n"
+ "@see $T\n",
GlideExtension.class,
glideType)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build())
.addMethods(
generateOverridesForGlideMethods(generatedCodePackageName, generatedRequestManager))
.build();
}
private List generateOverridesForGlideMethods(
final String generatedCodePackageName, final TypeSpec generatedRequestManager) {
return Lists.transform(
discoverGlideMethodsToOverride(),
new Function() {
@Override
public MethodSpec apply(ExecutableElement input) {
if (isGlideWithMethod(input)) {
return overrideGlideWithMethod(
generatedCodePackageName, generatedRequestManager, input);
} else {
return overrideGlideStaticMethod(input);
}
}
});
}
private MethodSpec overrideGlideStaticMethod(ExecutableElement methodToOverride) {
List parameters = processorUtil.getParameters(methodToOverride);
TypeElement element =
(TypeElement) processingEnv.getTypeUtils().asElement(methodToOverride.getReturnType());
MethodSpec.Builder builder =
MethodSpec.methodBuilder(methodToOverride.getSimpleName().toString())
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addJavadoc(processorUtil.generateSeeMethodJavadoc(methodToOverride))
.addParameters(parameters);
addReturnAnnotations(builder, methodToOverride);
boolean returnsValue = element != null;
if (returnsValue) {
builder.returns(ClassName.get(element));
}
StringBuilder code = new StringBuilder(returnsValue ? "return " : "");
code.append("$T.$N(");
List args = new ArrayList<>();
args.add(ClassName.get(glideType));
args.add(methodToOverride.getSimpleName());
if (!parameters.isEmpty()) {
for (ParameterSpec param : parameters) {
code.append("$L, ");
args.add(param.name);
}
code = new StringBuilder(code.substring(0, code.length() - 2));
}
code.append(")");
builder.addStatement(code.toString(), args.toArray(new Object[0]));
return builder.build();
}
private Builder addReturnAnnotations(Builder builder, ExecutableElement methodToOverride) {
Elements elements = processingEnv.getElementUtils();
TypeElement visibleForTestingTypeElement =
elements.getTypeElement(processorUtil.visibleForTesting().reflectionName());
String visibleForTestingTypeQualifiedName = visibleForTestingTypeElement.toString();
for (AnnotationMirror mirror : methodToOverride.getAnnotationMirrors()) {
builder.addAnnotation(AnnotationSpec.get(mirror));
// Suppress a lint warning if we're overriding a VisibleForTesting method.
// See #1977.
String annotationQualifiedName = mirror.getAnnotationType().toString();
if (annotationQualifiedName.equals(visibleForTestingTypeQualifiedName)) {
builder.addAnnotation(
AnnotationSpec.builder(
ClassName.get(SUPPRESS_LINT_PACKAGE_NAME, SUPPRESS_LINT_CLASS_NAME))
.addMember("value", "$S", "VisibleForTests")
.build());
}
}
return builder;
}
private List discoverGlideMethodsToOverride() {
return processorUtil.findStaticMethods(glideType);
}
private boolean isGlideWithMethod(ExecutableElement element) {
return processorUtil.isReturnValueTypeMatching(element, requestManagerType);
}
private MethodSpec overrideGlideWithMethod(
String packageName, TypeSpec generatedRequestManager, ExecutableElement methodToOverride) {
ClassName generatedRequestManagerClassName =
ClassName.get(packageName, generatedRequestManager.name);
List parameters = processorUtil.getParameters(methodToOverride);
Preconditions.checkArgument(
parameters.size() == 1, "Expected size of 1, but got %s", methodToOverride);
ParameterSpec parameter = parameters.iterator().next();
Builder builder =
MethodSpec.methodBuilder(methodToOverride.getSimpleName().toString())
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addJavadoc(processorUtil.generateSeeMethodJavadoc(methodToOverride))
.addParameters(parameters)
.returns(generatedRequestManagerClassName)
.addStatement(
"return ($T) $T.$N($L)",
generatedRequestManagerClassName,
glideType,
methodToOverride.getSimpleName().toString(),
parameter.name);
return addReturnAnnotations(builder, methodToOverride).build();
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/IndexerGenerator.java
================================================
package com.bumptech.glide.annotation.compiler;
import com.bumptech.glide.annotation.GlideExtension;
import com.bumptech.glide.annotation.GlideModule;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeSpec;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
/**
* Generates an empty class with an annotation containing the class names of one or more
* LibraryGlideModules and/or one or more GlideExtensions.
*
* We use a separate class so that LibraryGlideModules and GlideExtensions written in libraries
* can be bundled into an AAR and later retrieved by the annotation processor when it processes the
* AppGlideModule in an application.
*
*
The output file generated by this class with a LibraryGlideModule looks like this:
*
*
*
* {@literal @com.bumptech.glide.annotation.compiler.Index(}
* modules = "com.bumptech.glide.integration.okhttp3.OkHttpLibraryGlideModule"
* )
* public class Indexer_GlideModule_com_bumptech_glide_integration_okhttp3_OkHttpLibraryGlideModule
* {
* }
*
*
*
* The output file generated by this class with a GlideExtension looks like this:
*
*
*
* {@literal @com.bumptech.glide.annotation.compiler.Index(}
* extensions = "com.bumptech.glide.integration.gif.GifOptions"
* )
* public class Indexer_GlideExtension_com_bumptech_glide_integration_gif_GifOptions {
* }
*
*
*/
final class IndexerGenerator {
private static final String INDEXER_NAME_PREFIX = "GlideIndexer_";
private static final int MAXIMUM_FILE_NAME_LENGTH = 255;
private final ProcessorUtil processorUtil;
IndexerGenerator(ProcessorUtil processorUtil) {
this.processorUtil = processorUtil;
}
TypeSpec generate(List types) {
List modules = new ArrayList<>();
List extensions = new ArrayList<>();
for (TypeElement element : types) {
if (processorUtil.isExtension(element)) {
extensions.add(element);
} else if (processorUtil.isLibraryGlideModule(element)) {
modules.add(element);
} else {
throw new IllegalArgumentException("Unrecognized type: " + element);
}
}
if (!modules.isEmpty() && !extensions.isEmpty()) {
throw new IllegalArgumentException(
"Given both modules and extensions, expected one or the "
+ "other. Modules: "
+ modules
+ " Extensions: "
+ extensions);
}
if (!modules.isEmpty()) {
return generate(types, GlideModule.class);
} else {
return generate(types, GlideExtension.class);
}
}
private TypeSpec generate(
List libraryModules, Class extends Annotation> annotation) {
AnnotationSpec.Builder annotationBuilder = AnnotationSpec.builder(Index.class);
String value = getAnnotationValue(annotation);
for (TypeElement childModule : libraryModules) {
annotationBuilder.addMember(value, "$S", ClassName.get(childModule).toString());
}
StringBuilder indexerNameBuilder =
new StringBuilder(INDEXER_NAME_PREFIX + annotation.getSimpleName() + "_");
for (TypeElement element : libraryModules) {
indexerNameBuilder.append(element.getQualifiedName().toString().replace(".", "_"));
indexerNameBuilder.append("_");
}
indexerNameBuilder =
new StringBuilder(indexerNameBuilder.substring(0, indexerNameBuilder.length() - 1));
String indexerName = indexerNameBuilder.toString();
// If the indexer name has too many packages/modules, it can exceed the file name length
// allowed by the file system, which can break compilation. To avoid that, fall back to a
// deterministic UUID.
if (indexerName.length() >= (MAXIMUM_FILE_NAME_LENGTH - INDEXER_NAME_PREFIX.length())) {
indexerName =
INDEXER_NAME_PREFIX
+ UUID.nameUUIDFromBytes(indexerName.getBytes()).toString().replace("-", "_");
}
return TypeSpec.classBuilder(indexerName)
.addAnnotation(annotationBuilder.build())
.addModifiers(Modifier.PUBLIC)
.build();
}
private static String getAnnotationValue(Class extends Annotation> annotation) {
if (annotation == GlideModule.class) {
return "modules";
} else if (annotation == GlideExtension.class) {
return "extensions";
} else {
throw new IllegalArgumentException("Unrecognized annotation: " + annotation);
}
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/LibraryModuleProcessor.java
================================================
package com.bumptech.glide.annotation.compiler;
import com.bumptech.glide.annotation.GlideModule;
import com.squareup.javapoet.TypeSpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
/** Generates Indexer classes annotated with {@link Index} for all {@code LibraryGlideModule}s. */
final class LibraryModuleProcessor {
private final ProcessorUtil processorUtil;
private final IndexerGenerator indexerGenerator;
LibraryModuleProcessor(ProcessorUtil processorUtil, IndexerGenerator indexerGenerator) {
this.processorUtil = processorUtil;
this.indexerGenerator = indexerGenerator;
}
boolean processModules(RoundEnvironment env) {
// Order matters here, if we find an Indexer below, we return before writing the root module.
// If we fail to add to appModules before then, we might accidentally skip a valid RootModule.
List libraryGlideModules = new ArrayList<>();
for (TypeElement element : processorUtil.getElementsFor(GlideModule.class, env)) {
// Root elements are added separately and must be checked separately because they're sub
// classes of LibraryGlideModules.
if (processorUtil.isAppGlideModule(element)) {
continue;
} else if (!processorUtil.isLibraryGlideModule(element)) {
throw new IllegalStateException(
"@GlideModule can only be applied to LibraryGlideModule"
+ " and AppGlideModule implementations, not: "
+ element);
}
libraryGlideModules.add(element);
}
processorUtil.debugLog("got child modules: " + libraryGlideModules);
if (libraryGlideModules.isEmpty()) {
return false;
}
TypeSpec indexer = indexerGenerator.generate(libraryGlideModules);
processorUtil.writeIndexer(indexer);
processorUtil.debugLog(
"Wrote an Indexer this round, skipping the app module to ensure all "
+ "indexers are found");
// If I write an Indexer in a round in the target package, then try to find all classes in
// the target package, my newly written Indexer won't be found. Since we wrote a class with
// an Annotation handled by this processor, we know we will be called again in the next round
// and we can safely wait to write our AppGlideModule until then.
return true;
}
Set getSupportedAnnotationTypes() {
return Collections.singleton(GlideModule.class.getName());
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/ProcessorUtil.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.GlideAnnotationProcessor.DEBUG;
import com.bumptech.glide.annotation.GlideExtension;
import com.bumptech.glide.annotation.GlideOption;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import javax.annotation.Nullable;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
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.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
/** Utilities for writing classes and logging. */
final class ProcessorUtil {
private static final String GLIDE_MODULE_PACKAGE_NAME = "com.bumptech.glide.module";
private static final String APP_GLIDE_MODULE_SIMPLE_NAME = "AppGlideModule";
private static final String LIBRARY_GLIDE_MODULE_SIMPLE_NAME = "LibraryGlideModule";
private static final String APP_GLIDE_MODULE_QUALIFIED_NAME =
GLIDE_MODULE_PACKAGE_NAME + "." + APP_GLIDE_MODULE_SIMPLE_NAME;
private static final String LIBRARY_GLIDE_MODULE_QUALIFIED_NAME =
GLIDE_MODULE_PACKAGE_NAME + "." + LIBRARY_GLIDE_MODULE_SIMPLE_NAME;
private static final String COMPILER_PACKAGE_NAME =
GlideAnnotationProcessor.class.getPackage().getName();
private static final ClassName SUPPORT_NONNULL_ANNOTATION =
ClassName.get("android.support.annotation", "NonNull");
private static final ClassName JETBRAINS_NOTNULL_ANNOTATION =
ClassName.get("org.jetbrains.annotations", "NotNull");
private static final ClassName ANDROIDX_NONNULL_ANNOTATION =
ClassName.get("androidx.annotation", "NonNull");
private static final ClassName SUPPORT_CHECK_RESULT_ANNOTATION =
ClassName.get("android.support.annotation", "CheckResult");
private static final ClassName ANDROIDX_CHECK_RESULT_ANNOTATION =
ClassName.get("androidx.annotation", "CheckResult");
private static final ClassName SUPPORT_VISIBLE_FOR_TESTING =
ClassName.get("android.support.annotation", "VisibleForTesting");
private static final ClassName ANDROIDX_VISIBLE_FOR_TESTING =
ClassName.get("androidx.annotation", "VisibleForTesting");
private final ProcessingEnvironment processingEnv;
private final TypeElement appGlideModuleType;
private final TypeElement libraryGlideModuleType;
private int round;
ProcessorUtil(ProcessingEnvironment processingEnv) {
this.processingEnv = processingEnv;
appGlideModuleType =
processingEnv.getElementUtils().getTypeElement(APP_GLIDE_MODULE_QUALIFIED_NAME);
libraryGlideModuleType =
processingEnv.getElementUtils().getTypeElement(LIBRARY_GLIDE_MODULE_QUALIFIED_NAME);
}
void process() {
round++;
}
boolean isAppGlideModule(TypeElement element) {
return processingEnv.getTypeUtils().isAssignable(element.asType(), appGlideModuleType.asType());
}
boolean isLibraryGlideModule(TypeElement element) {
return processingEnv
.getTypeUtils()
.isAssignable(element.asType(), libraryGlideModuleType.asType());
}
boolean isExtension(TypeElement element) {
return element.getAnnotation(GlideExtension.class) != null;
}
int getOverrideType(ExecutableElement element) {
GlideOption glideOption = element.getAnnotation(GlideOption.class);
return glideOption.override();
}
void writeIndexer(TypeSpec indexer) {
writeClass(COMPILER_PACKAGE_NAME, indexer);
}
void writeClass(String packageName, TypeSpec clazz) {
try {
debugLog("Writing class:\n" + clazz);
JavaFile.builder(packageName, clazz)
.skipJavaLangImports(true)
.build()
.writeTo(processingEnv.getFiler());
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
List findAnnotatedElementsInClasses(
Set classNames, Class extends Annotation> annotationClass) {
List result = new ArrayList<>();
for (String glideExtensionClassName : classNames) {
TypeElement glideExtension =
processingEnv.getElementUtils().getTypeElement(glideExtensionClassName);
for (Element element : glideExtension.getEnclosedElements()) {
if (element.getAnnotation(annotationClass) != null) {
result.add((ExecutableElement) element);
}
}
}
return result;
}
List getElementsFor(Class extends Annotation> clazz, RoundEnvironment env) {
Collection extends Element> annotatedElements = env.getElementsAnnotatedWith(clazz);
return ElementFilter.typesIn(annotatedElements);
}
/**
* Generates a Javadoc code block for generated methods that delegate to methods in {@link
* GlideExtension}s.
*
* The generated block looks something like this:
*
*
*
* {@literal @see} com.extension.package.name.ExtensionClassName#extensionMethod(arg1, argN)
*
*
*
* @param method The method from the {@link GlideExtension} annotated class that the generated
* method this Javadoc will be attached to delegates to.
*/
CodeBlock generateSeeMethodJavadoc(ExecutableElement method) {
// Use the simple name of the containing type instead of just the containing type's TypeMirror
// so that we avoid appending or other type arguments to the class and breaking
// Javadoc's linking.
// With this we get @see RequestOptions#methodName().
// With just ClassName.get(element.getEnclosingElement().asType()), we get:
// @see RequestOptions#methodName().
return generateSeeMethodJavadoc(
getJavadocSafeName(method.getEnclosingElement()),
method.getSimpleName().toString(),
method.getParameters());
}
/**
* Generates a Javadoc block for generated methods that delegate to other methods.
*
* The generated block looks something like this:
*
*
*
* {@literal @see} com.package.ClassContainingMethod.methodSimpleName(
* methodParam1, methodParamN)
*
*
*
* @param nameOfClassContainingMethod The simple class name of the class containing the method
* without any generic types like {@literal }.
* @param methodSimpleName The name of the method.
* @param methodParameters A maybe empty list of all the parameters for the method in question.
*/
CodeBlock generateSeeMethodJavadoc(
TypeName nameOfClassContainingMethod,
String methodSimpleName,
List extends VariableElement> methodParameters) {
return generateSeeMethodJavadocInternal(
nameOfClassContainingMethod,
methodSimpleName,
Lists.transform(
methodParameters,
new Function() {
@Override
public Object apply(VariableElement input) {
return getJavadocSafeName(input);
}
}));
}
CodeBlock generateSeeMethodJavadoc(TypeName nameOfClassContainingMethod, MethodSpec methodSpec) {
return generateSeeMethodJavadocInternal(
nameOfClassContainingMethod,
methodSpec.name,
Lists.transform(
methodSpec.parameters,
new Function() {
@Override
public Object apply(ParameterSpec input) {
return input.type;
}
}));
}
private CodeBlock generateSeeMethodJavadocInternal(
TypeName nameOfClassContainingMethod, String methodName, List safeParameterNames) {
StringBuilder javadocString = new StringBuilder("@see $T#$L(");
List javadocArgs = new ArrayList<>();
javadocArgs.add(nameOfClassContainingMethod);
javadocArgs.add(methodName);
for (Object param : safeParameterNames) {
javadocString.append("$T, ");
javadocArgs.add(param);
}
if (javadocArgs.size() > 2) {
javadocString = new StringBuilder(javadocString.substring(0, javadocString.length() - 2));
}
javadocString.append(")\n");
return CodeBlock.of(javadocString.toString(), javadocArgs.toArray(new Object[0]));
}
/**
* Returns a safe String to use in a Javadoc that will function in a link.
*
* This method exists because by Javadoc doesn't handle type parameters({@literal } in
* {@literal RequestOptions} for example).
*/
private TypeName getJavadocSafeName(Element element) {
Types typeUtils = processingEnv.getTypeUtils();
TypeMirror type = element.asType();
if (typeUtils.asElement(type) == null) {
// If there is no Element, it's a primitive and can't have additional types, so we're done.
return ClassName.get(element.asType());
}
Name simpleName = typeUtils.asElement(type).getSimpleName();
return ClassName.bestGuess(simpleName.toString());
}
void debugLog(String toLog) {
if (DEBUG) {
infoLog(toLog);
}
}
void infoLog(String toLog) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "[" + round + "] " + toLog);
}
static CodeBlock generateCastingSuperCall(TypeName toReturn, MethodSpec method) {
return CodeBlock.builder()
.add("return ($T) super.$N(", toReturn, method.name)
.add(
FluentIterable.from(method.parameters)
.transform(
new Function() {
@Override
public String apply(ParameterSpec input) {
return input.name;
}
})
.join(Joiner.on(",")))
.add(");\n")
.build();
}
MethodSpec.Builder overriding(ExecutableElement method) {
String methodName = method.getSimpleName().toString();
MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName).addAnnotation(Override.class);
Set modifiers = method.getModifiers();
modifiers = new LinkedHashSet<>(modifiers);
modifiers.remove(Modifier.ABSTRACT);
Modifier defaultModifier = null;
// Modifier.DEFAULT doesn't exist until Java 8.
try {
defaultModifier = Modifier.valueOf("DEFAULT");
} catch (IllegalArgumentException e) {
// Ignored.
}
modifiers.remove(defaultModifier);
builder = builder.addModifiers(modifiers);
for (TypeParameterElement typeParameterElement : method.getTypeParameters()) {
TypeVariable var = (TypeVariable) typeParameterElement.asType();
builder = builder.addTypeVariable(TypeVariableName.get(var));
}
builder =
builder
.returns(TypeName.get(method.getReturnType()))
.addParameters(getParameters(method))
.varargs(method.isVarArgs());
for (TypeMirror thrownType : method.getThrownTypes()) {
builder = builder.addException(TypeName.get(thrownType));
}
return builder;
}
List getParameters(ExecutableElement method) {
return getParameters(method.getParameters());
}
List getParameters(List extends VariableElement> parameters) {
List result = new ArrayList<>();
for (VariableElement parameter : parameters) {
result.add(getParameter(parameter));
}
return dedupedParameters(result);
}
private static List dedupedParameters(List parameters) {
boolean hasDupes = false;
Set names = new HashSet<>();
for (ParameterSpec parameter : parameters) {
String name = parameter.name;
if (names.contains(name)) {
hasDupes = true;
} else {
names.add(name);
}
}
if (hasDupes) {
List copy = parameters;
parameters = new ArrayList<>();
for (int i = 0; i < copy.size(); i++) {
ParameterSpec parameter = copy.get(i);
parameters.add(
ParameterSpec.builder(parameter.type, parameter.name + i)
.addModifiers(parameter.modifiers)
.addAnnotations(parameter.annotations)
.build());
}
}
return parameters;
}
private ParameterSpec getParameter(VariableElement parameter) {
TypeName type = TypeName.get(parameter.asType());
return ParameterSpec.builder(type, computeParameterName(parameter, type))
.addModifiers(parameter.getModifiers())
.addAnnotations(getAnnotations(parameter))
.build();
}
private static String computeParameterName(VariableElement parameter, TypeName type) {
String rawClassName = type.withoutAnnotations().toString();
String name;
if (type.isPrimitive() || type.isBoxedPrimitive()) {
name = getSmartPrimitiveParameterName(parameter);
} else {
if (rawClassName.contains("<") && rawClassName.contains(">")) {
String[] preGenericSplit = rawClassName.split("<");
String preGeneric = preGenericSplit[0];
String[] postGenericSplit = rawClassName.split(">");
String postGeneric = postGenericSplit[postGenericSplit.length - 1];
if (postGenericSplit.length > 1) {
rawClassName = preGeneric + postGeneric;
} else {
rawClassName = preGeneric;
}
}
String[] qualifiers = rawClassName.split("\\.");
rawClassName = qualifiers[qualifiers.length - 1];
rawClassName = applySmartParameterNameReplacements(rawClassName);
boolean allCaps = true;
for (char c : rawClassName.toCharArray()) {
if (Character.isLowerCase(c)) {
allCaps = false;
break;
}
}
if (allCaps) {
name = rawClassName.toLowerCase(Locale.ROOT);
} else {
int indexOfLastWordStart = 0;
char[] chars = rawClassName.toCharArray();
for (int i = 0, charArrayLength = chars.length; i < charArrayLength; i++) {
char c = chars[i];
if (Character.isUpperCase(c)) {
indexOfLastWordStart = i;
}
}
rawClassName = rawClassName.substring(indexOfLastWordStart, rawClassName.length());
name =
Character.toLowerCase(rawClassName.charAt(0))
+ rawClassName.substring(1, rawClassName.length());
}
}
return name;
}
private static String getSmartPrimitiveParameterName(VariableElement parameter) {
for (AnnotationMirror annotation : parameter.getAnnotationMirrors()) {
String annotationName = annotation.getAnnotationType().toString().toUpperCase(Locale.ROOT);
if (annotationName.endsWith("RES")) {
// Catch annotations like StringRes
return "id";
} else if (annotationName.endsWith("RANGE")) {
// Catch annotations like IntRange
return "value";
}
}
return parameter.getSimpleName().toString();
}
private static String applySmartParameterNameReplacements(String name) {
name = name.replace("[]", "s");
name = name.replace(Class.class.getSimpleName(), "clazz");
name = name.replace(Object.class.getSimpleName(), "o");
return name;
}
private List getAnnotations(VariableElement element) {
List result = new ArrayList<>();
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
result.add(maybeConvertSupportLibraryAnnotation(mirror));
}
return result;
}
private AnnotationSpec maybeConvertSupportLibraryAnnotation(AnnotationMirror mirror) {
String annotationName = mirror.getAnnotationType().asElement().toString();
boolean preferAndroidX = visibleForTesting().equals(ANDROIDX_VISIBLE_FOR_TESTING);
ImmutableBiMap map =
ImmutableBiMap.builder()
.put(SUPPORT_NONNULL_ANNOTATION, ANDROIDX_NONNULL_ANNOTATION)
.put(SUPPORT_CHECK_RESULT_ANNOTATION, ANDROIDX_CHECK_RESULT_ANNOTATION)
.put(SUPPORT_VISIBLE_FOR_TESTING, ANDROIDX_VISIBLE_FOR_TESTING)
.build();
ClassName remapped = null;
if (preferAndroidX && annotationName.startsWith("android.support.annotation")) {
remapped = ClassName.get((TypeElement) mirror.getAnnotationType().asElement());
} else if (!preferAndroidX && annotationName.startsWith("androidx.annotation")) {
remapped = ClassName.get((TypeElement) mirror.getAnnotationType().asElement());
}
if (remapped != null && map.containsKey(remapped)) {
return AnnotationSpec.builder(map.get(remapped)).build();
} else {
return AnnotationSpec.get(mirror);
}
}
ClassName visibleForTesting() {
return findAnnotationClassName(ANDROIDX_VISIBLE_FOR_TESTING, SUPPORT_VISIBLE_FOR_TESTING);
}
ClassName nonNull() {
return findAnnotationClassName(ANDROIDX_NONNULL_ANNOTATION, SUPPORT_NONNULL_ANNOTATION);
}
ClassName checkResult() {
return findAnnotationClassName(
ANDROIDX_CHECK_RESULT_ANNOTATION, SUPPORT_CHECK_RESULT_ANNOTATION);
}
static List nonNulls() {
return ImmutableList.of(
SUPPORT_NONNULL_ANNOTATION, JETBRAINS_NOTNULL_ANNOTATION, ANDROIDX_NONNULL_ANNOTATION);
}
private ClassName findAnnotationClassName(ClassName androidxName, ClassName supportName) {
Elements elements = processingEnv.getElementUtils();
TypeElement visibleForTestingTypeElement =
elements.getTypeElement(androidxName.reflectionName());
if (visibleForTestingTypeElement != null) {
return androidxName;
}
return supportName;
}
List findInstanceMethodsReturning(TypeElement clazz, TypeMirror returnType) {
return FluentIterable.from(clazz.getEnclosedElements())
.filter(new FilterPublicMethods(returnType, MethodType.INSTANCE))
.transform(new ToMethod())
.toList();
}
List findInstanceMethodsReturning(TypeElement clazz, TypeElement returnType) {
return FluentIterable.from(clazz.getEnclosedElements())
.filter(new FilterPublicMethods(returnType, MethodType.INSTANCE))
.transform(new ToMethod())
.toList();
}
List findStaticMethodsReturning(TypeElement clazz, TypeElement returnType) {
return FluentIterable.from(clazz.getEnclosedElements())
.filter(new FilterPublicMethods(returnType, MethodType.STATIC))
.transform(new ToMethod())
.toList();
}
List findStaticMethods(TypeElement clazz) {
return FluentIterable.from(clazz.getEnclosedElements())
.filter(new FilterPublicMethods((TypeMirror) null /*returnType*/, MethodType.STATIC))
.transform(new ToMethod())
.toList();
}
ImmutableSet findClassValuesFromAnnotationOnClassAsNames(
Element clazz, Class extends Annotation> annotationClass) {
String annotationClassName = annotationClass.getName();
AnnotationValue excludedModuleAnnotationValue = null;
for (AnnotationMirror annotationMirror : clazz.getAnnotationMirrors()) {
// Two different AnnotationMirrors the same class might not be equal, so compare Strings
// instead. This check is necessary because a given class may have multiple Annotations.
if (!annotationClassName.equals(annotationMirror.getAnnotationType().toString())) {
continue;
}
var entries = annotationMirror.getElementValues().entrySet();
if (entries.size() != 1) {
throw new IllegalArgumentException("Expected single value, but found: " + entries);
}
excludedModuleAnnotationValue = entries.iterator().next().getValue();
if (excludedModuleAnnotationValue == null) {
throw new IllegalArgumentException(
"Failed to find value for: "
+ annotationClass
+ " from mirrors: "
+ clazz.getAnnotationMirrors());
}
}
if (excludedModuleAnnotationValue == null) {
return ImmutableSet.of();
}
Object value = excludedModuleAnnotationValue.getValue();
if (value instanceof List) {
LinkedHashSet out = new LinkedHashSet<>();
for (Object o : (List>) value) {
AnnotationValue av = (AnnotationValue) o;
out.add(qualifiedNameFromTypeMirror((TypeMirror) av.getValue()));
}
return ImmutableSet.copyOf(out);
} else {
return ImmutableSet.of(qualifiedNameFromTypeMirror((TypeMirror) value));
}
}
static String qualifiedNameFromTypeMirror(TypeMirror type) {
if (type.getKind() == TypeKind.ERROR) {
throw new IllegalArgumentException("Unresolved class type in annotation: " + type);
}
if (type.getKind() == TypeKind.DECLARED) {
DeclaredType dt = (DeclaredType) type;
TypeElement te = (TypeElement) dt.asElement();
return te.getQualifiedName().toString();
}
return type.toString();
}
private enum MethodType {
STATIC,
INSTANCE
}
private final class FilterPublicMethods implements Predicate {
@Nullable private final TypeMirror returnType;
private final MethodType methodType;
FilterPublicMethods(@Nullable TypeMirror returnType, MethodType methodType) {
this.returnType = returnType;
this.methodType = methodType;
}
FilterPublicMethods(@Nullable TypeElement returnType, MethodType methodType) {
this(returnType != null ? returnType.asType() : null, methodType);
}
@Override
public boolean apply(@Nullable Element input) {
if (input == null
|| input.getKind() != ElementKind.METHOD
|| !input.getModifiers().contains(Modifier.PUBLIC)) {
return false;
}
boolean isStatic = input.getModifiers().contains(Modifier.STATIC);
if (methodType == MethodType.STATIC && !isStatic) {
return false;
} else if (methodType == MethodType.INSTANCE && isStatic) {
return false;
}
ExecutableElement method = (ExecutableElement) input;
return returnType == null || isReturnValueTypeMatching(method, returnType);
}
}
boolean isReturnValueTypeMatching(ExecutableElement method, TypeElement expectedReturnType) {
return isReturnValueTypeMatching(method, expectedReturnType.asType());
}
private boolean isReturnValueTypeMatching(
ExecutableElement method, TypeMirror expectedReturnType) {
return processingEnv.getTypeUtils().isAssignable(method.getReturnType(), expectedReturnType);
}
private static final class ToMethod implements Function {
@Nullable
@Override
public ExecutableElement apply(@Nullable Element input) {
return (ExecutableElement) input;
}
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/RequestBuilderGenerator.java
================================================
package com.bumptech.glide.annotation.compiler;
import com.bumptech.glide.annotation.GlideExtension;
import com.bumptech.glide.annotation.GlideOption;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import com.squareup.javapoet.WildcardTypeName;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
/**
* Generates a {@code com.bumptech.glide.RequestBuilder} subclass containing all methods from the
* base class, all methods from {@code com.bumptech.glide.request.RequestOptions} and all
* non-override {@link GlideOption} annotated methods in {@link GlideExtension} annotated classes.
*
* Generated code looks like this:
*
*
*
* public final class GlideRequest extends RequestBuilder {
* GlideRequest(Class transcodeClass, RequestBuilder> other) {
* super(transcodeClass, other);
* }
*
* GlideRequest(GlideContext context, RequestManager requestManager,
* Class transcodeClass) {
* super(context, requestManager ,transcodeClass);
* }
*
* {@literal @Override}
* protected GlideRequest getDownloadOnlyRequest() {
* return new GlideRequest<>(File.class, this).apply(DOWNLOAD_ONLY_OPTIONS);
* }
*
* /**
* * {@literal @see} GlideOptions#dontAnimate()
* *\/
* public GlideRequest dontAnimate() {
* if (getMutableOptions() instanceof GlideOptions) {
* this.requestOptions = ((GlideOptions) getMutableOptions()).dontAnimate();
* } else {
* this.requestOptions = new GlideOptions().apply(this.requestOptions).dontAnimate();
* }
* return this;
* }
*
* /**
* * {@literal @see} RequestOptions#sizeMultiplier(float)
* *\/
* public GlideRequest sizeMultiplier(float sizeMultiplier) {
* this.requestOptions = getMutableOptions().sizeMultiplier(sizeMultiplier);
* return this;
* }
*
* ...
* }
*
*
*/
final class RequestBuilderGenerator {
private static final String REQUEST_OPTIONS_PACKAGE_NAME = "com.bumptech.glide.request";
private static final String REQUEST_OPTIONS_SIMPLE_NAME = "RequestOptions";
private static final String REQUEST_OPTIONS_QUALIFIED_NAME =
REQUEST_OPTIONS_PACKAGE_NAME + "." + REQUEST_OPTIONS_SIMPLE_NAME;
private static final String REQUEST_BUILDER_PACKAGE_NAME = "com.bumptech.glide";
private static final String REQUEST_BUILDER_SIMPLE_NAME = "RequestBuilder";
static final String REQUEST_BUILDER_QUALIFIED_NAME =
REQUEST_BUILDER_PACKAGE_NAME + "." + REQUEST_BUILDER_SIMPLE_NAME;
// Uses package private methods and variables.
private static final String GENERATED_REQUEST_BUILDER_SIMPLE_NAME = "GlideRequest";
/**
* An arbitrary name of the Generic type in the generated RequestBuilder. e.g.
* RequestBuilder
*/
private static final String TRANSCODE_TYPE_NAME = "TranscodeType";
/** A set of method names to avoid overriding from RequestOptions. */
private static final ImmutableSet EXCLUDED_METHODS_FROM_BASE_REQUEST_OPTIONS =
ImmutableSet.of("clone", "apply");
private final ProcessingEnvironment processingEnv;
private final ProcessorUtil processorUtil;
private final TypeVariableName transcodeTypeName;
private final TypeElement requestOptionsType;
private final TypeElement requestBuilderType;
private ClassName generatedRequestBuilderClassName;
private ClassName requestOptionsClassName;
private ParameterizedTypeName generatedRequestBuilderOfTranscodeType;
RequestBuilderGenerator(ProcessingEnvironment processingEnv, ProcessorUtil processorUtil) {
this.processingEnv = processingEnv;
this.processorUtil = processorUtil;
requestBuilderType =
processingEnv.getElementUtils().getTypeElement(REQUEST_BUILDER_QUALIFIED_NAME);
transcodeTypeName = TypeVariableName.get(TRANSCODE_TYPE_NAME);
requestOptionsType =
processingEnv.getElementUtils().getTypeElement(REQUEST_OPTIONS_QUALIFIED_NAME);
}
TypeSpec generate(
String generatedCodePackageName,
Set glideExtensionClassNames,
@Nullable TypeSpec generatedOptions) {
if (generatedOptions != null) {
requestOptionsClassName = ClassName.get(generatedCodePackageName, generatedOptions.name);
} else {
requestOptionsClassName =
ClassName.get(
RequestOptionsGenerator.REQUEST_OPTIONS_PACKAGE_NAME,
RequestOptionsGenerator.BASE_REQUEST_OPTIONS_SIMPLE_NAME);
}
generatedRequestBuilderClassName =
ClassName.get(generatedCodePackageName, GENERATED_REQUEST_BUILDER_SIMPLE_NAME);
generatedRequestBuilderOfTranscodeType =
ParameterizedTypeName.get(generatedRequestBuilderClassName, transcodeTypeName);
RequestOptionsExtensionGenerator requestOptionsExtensionGenerator =
new RequestOptionsExtensionGenerator(generatedRequestBuilderOfTranscodeType, processorUtil);
ParameterizedTypeName requestBuilderOfTranscodeType =
ParameterizedTypeName.get(
ClassName.get(REQUEST_BUILDER_PACKAGE_NAME, REQUEST_BUILDER_SIMPLE_NAME),
transcodeTypeName);
List requestOptionsExtensionMethods =
requestOptionsExtensionGenerator.generateInstanceMethodsForExtensions(
glideExtensionClassNames);
return TypeSpec.classBuilder(GENERATED_REQUEST_BUILDER_SIMPLE_NAME)
.addJavadoc(
"Contains all public methods from {@link $T}, all options from\n", requestBuilderType)
.addJavadoc("{@link $T} and all generated options from\n", requestOptionsType)
.addJavadoc("{@link $T} in annotated methods in\n", GlideOption.class)
.addJavadoc("{@link $T} annotated classes.\n", GlideExtension.class)
.addJavadoc("\n")
.addJavadoc("Generated code, do not modify.\n")
.addJavadoc("\n")
.addJavadoc("@see $T\n", requestBuilderType)
.addJavadoc("@see $T\n", requestOptionsType)
.addAnnotation(
AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "unused")
.addMember("value", "$S", "deprecation")
.build())
.addModifiers(Modifier.PUBLIC)
.addTypeVariable(transcodeTypeName)
.superclass(requestBuilderOfTranscodeType)
.addSuperinterface(Cloneable.class)
.addMethods(generateConstructors())
.addMethod(generateDownloadOnlyRequestMethod())
.addMethods(
generateGeneratedRequestOptionsEquivalents(
requestOptionsExtensionMethods, generatedOptions))
.addMethods(generateRequestBuilderOverrides())
.addMethods(requestOptionsExtensionMethods)
.build();
}
/**
* Generates methods with equivalent names and arguments to methods annotated with {@link
* GlideOption} in {@link com.bumptech.glide.annotation.GlideExtension}s that return our generated
* {@code com.bumptech.glide.RequestBuilder} subclass.
*/
private List generateGeneratedRequestOptionsEquivalents(
final List requestOptionsExtensionMethods,
@Nullable final TypeSpec generatedOptions) {
if (generatedOptions == null) {
return Collections.emptyList();
}
return FluentIterable.from(generatedOptions.methodSpecs)
.filter(
new Predicate() {
@Override
public boolean apply(MethodSpec input) {
return isUsefulGeneratedRequestOption(requestOptionsExtensionMethods, input);
}
})
.transform(
new Function() {
@Override
public MethodSpec apply(MethodSpec input) {
return generateGeneratedRequestOptionEquivalent(input);
}
})
.toList();
}
/**
* Returns {@code true} if the given {@link MethodSpec} is a useful method to have in our {@code
* com.bumptech.glide.RequestBuilder} subclass.
*
* Only newly generated methods will be included in the generated {@code
* com.bumptech.glide.request.BaseRequestBuilder} subclass, so we only have to filter out methods
* that override other methods to avoid duplicates.
*/
private boolean isUsefulGeneratedRequestOption(
List requestOptionsExtensionMethods, final MethodSpec requestOptionsMethod) {
return !EXCLUDED_METHODS_FROM_BASE_REQUEST_OPTIONS.contains(requestOptionsMethod.name)
&& requestOptionsMethod.hasModifier(Modifier.PUBLIC)
&& !requestOptionsMethod.hasModifier(Modifier.STATIC)
&& requestOptionsMethod.returnType.toString().equals(requestOptionsClassName.toString())
&& !isExtensionMethod(requestOptionsExtensionMethods, requestOptionsMethod);
}
private boolean isExtensionMethod(
List requestOptionsExtensionMethods, final MethodSpec requestOptionsMethod) {
return FluentIterable.from(requestOptionsExtensionMethods)
.anyMatch(
new Predicate() {
@Override
public boolean apply(MethodSpec input) {
return input.name.equals(requestOptionsMethod.name)
&& input.parameters.equals(requestOptionsMethod.parameters);
}
});
}
/**
* Generates a particular method with an equivalent name and arguments to the given method from
* the generated {@code com.bumptech.glide.request.BaseRequestBuilder} subclass.
*/
private MethodSpec generateGeneratedRequestOptionEquivalent(MethodSpec requestOptionMethod) {
CodeBlock callRequestOptionsMethod =
CodeBlock.builder()
.add(".$N(", requestOptionMethod.name)
.add(
FluentIterable.from(requestOptionMethod.parameters)
.transform(
new Function() {
@Override
public String apply(ParameterSpec input) {
return input.name;
}
})
.join(Joiner.on(", ")))
.add(");\n")
.build();
MethodSpec.Builder result =
MethodSpec.methodBuilder(requestOptionMethod.name)
.addJavadoc(
processorUtil.generateSeeMethodJavadoc(
requestOptionsClassName, requestOptionMethod))
.addModifiers(Modifier.PUBLIC)
.varargs(requestOptionMethod.varargs)
.addAnnotations(
FluentIterable.from(requestOptionMethod.annotations)
.filter(
new Predicate() {
@Override
public boolean apply(AnnotationSpec input) {
return !input.type.equals(TypeName.get(Override.class))
// SafeVarargs can only be applied to final methods. GlideRequest is
// non-final to allow for mocking.
&& !input.type.equals(TypeName.get(SafeVarargs.class))
// We need to combine warnings below.
&& !input.type.equals(TypeName.get(SuppressWarnings.class));
}
})
.toList())
.addTypeVariables(requestOptionMethod.typeVariables)
.addParameters(requestOptionMethod.parameters)
.returns(generatedRequestBuilderOfTranscodeType)
.addCode("return ($T) super", generatedRequestBuilderOfTranscodeType)
.addCode(callRequestOptionsMethod);
AnnotationSpec suppressWarnings = buildSuppressWarnings(requestOptionMethod);
if (suppressWarnings != null) {
result.addAnnotation(suppressWarnings);
}
return result.build();
}
@Nullable
private AnnotationSpec buildSuppressWarnings(MethodSpec requestOptionMethod) {
Set suppressions = new HashSet<>();
if (requestOptionMethod.annotations.contains(
AnnotationSpec.builder(SuppressWarnings.class).build())) {
for (AnnotationSpec annotation : requestOptionMethod.annotations) {
if (annotation.type.equals(TypeName.get(SuppressWarnings.class))) {
List codeBlocks = annotation.members.get("value");
suppressions.addAll(
FluentIterable.from(codeBlocks)
.transform(
new Function() {
@Override
public String apply(CodeBlock input) {
return input.toString();
}
})
.toSet());
}
}
}
if (requestOptionMethod.annotations.contains(
AnnotationSpec.builder(SafeVarargs.class).build())) {
suppressions.add("unchecked");
suppressions.add("varargs");
}
if (suppressions.isEmpty()) {
return null;
}
// Enforce ordering across compilers (Internal and External compilers end up disagreeing on the
// order produced by the Set additions above.)
ArrayList suppressionsList = new ArrayList<>(suppressions);
Collections.sort(suppressionsList);
AnnotationSpec.Builder builder = AnnotationSpec.builder(SuppressWarnings.class);
for (String suppression : suppressionsList) {
builder.addMember("value", "$S", suppression);
}
return builder.build();
}
/**
* Generates overrides of all methods in {@code com.bumptech.glide.RequestBuilder} that return
* {@code com.bumptech.glide.RequestBuilder} so that they return our generated subclass instead.
*/
private List generateRequestBuilderOverrides() {
TypeMirror rawRequestBuilderType =
processingEnv.getTypeUtils().erasure(requestBuilderType.asType());
return Lists.transform(
processorUtil.findInstanceMethodsReturning(requestBuilderType, rawRequestBuilderType),
new Function() {
@Override
public MethodSpec apply(ExecutableElement input) {
return generateRequestBuilderOverride(input);
}
});
}
/**
* Generates an override of a particular method in {@code com.bumptech.glide.RequestBuilder} that
* returns {@code com.bumptech.glide.RequestBuilder} so that it returns our generated subclass
* instead.
*/
private MethodSpec generateRequestBuilderOverride(ExecutableElement methodToOverride) {
// We've already verified that this method returns a RequestBuilder and RequestBuilders have
// exactly one type argument, so this is safe unless those assumptions change.
TypeMirror typeArgument =
((DeclaredType) methodToOverride.getReturnType()).getTypeArguments().get(0);
ParameterizedTypeName generatedRequestBuilderOfType =
ParameterizedTypeName.get(generatedRequestBuilderClassName, ClassName.get(typeArgument));
MethodSpec.Builder builder =
processorUtil.overriding(methodToOverride).returns(generatedRequestBuilderOfType);
builder.addCode(
CodeBlock.builder()
.add(
"return ($T) super.$N(",
generatedRequestBuilderOfType,
methodToOverride.getSimpleName())
.add(
FluentIterable.from(builder.build().parameters)
.transform(
new Function() {
@Override
public String apply(ParameterSpec input) {
return input.name;
}
})
.join(Joiner.on(", ")))
.add(");\n")
.build());
for (AnnotationMirror mirror : methodToOverride.getAnnotationMirrors()) {
builder = builder.addAnnotation(AnnotationSpec.get(mirror));
}
if (methodToOverride.isVarArgs()) {
builder =
builder
.addModifiers(Modifier.FINAL)
.addAnnotation(SafeVarargs.class)
.addAnnotation(
AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "varargs")
.build());
}
return builder.build();
}
private List generateConstructors() {
ParameterizedTypeName classOfTranscodeType =
ParameterizedTypeName.get(ClassName.get(Class.class), transcodeTypeName);
TypeName wildcardOfObject = WildcardTypeName.subtypeOf(Object.class);
ParameterizedTypeName requestBuilderOfWildcardOfObject =
ParameterizedTypeName.get(ClassName.get(requestBuilderType), wildcardOfObject);
MethodSpec firstConstructor =
MethodSpec.constructorBuilder()
.addParameter(
ParameterSpec.builder(classOfTranscodeType, "transcodeClass")
.addAnnotation(processorUtil.nonNull())
.build())
.addParameter(
ParameterSpec.builder(requestBuilderOfWildcardOfObject, "other")
.addAnnotation(processorUtil.nonNull())
.build())
.addStatement("super($N, $N)", "transcodeClass", "other")
.build();
ClassName context = ClassName.get("android.content", "Context");
ClassName glide = ClassName.get("com.bumptech.glide", "Glide");
ClassName requestManager = ClassName.get("com.bumptech.glide", "RequestManager");
MethodSpec secondConstructor =
MethodSpec.constructorBuilder()
.addParameter(
ParameterSpec.builder(glide, "glide")
.addAnnotation(processorUtil.nonNull())
.build())
.addParameter(
ParameterSpec.builder(requestManager, "requestManager")
.addAnnotation(processorUtil.nonNull())
.build())
.addParameter(
ParameterSpec.builder(classOfTranscodeType, "transcodeClass")
.addAnnotation(processorUtil.nonNull())
.build())
.addParameter(
ParameterSpec.builder(context, "context")
.addAnnotation(processorUtil.nonNull())
.build())
.addStatement(
"super($N, $N ,$N, $N)", "glide", "requestManager", "transcodeClass", "context")
.build();
return ImmutableList.of(firstConstructor, secondConstructor);
}
/**
* Overrides the protected downloadOnly method in {@code com.bumptech.glide.RequestBuilder} to
* return our generated subclass instead.
*/
private MethodSpec generateDownloadOnlyRequestMethod() {
ParameterizedTypeName generatedRequestBuilderOfFile =
ParameterizedTypeName.get(generatedRequestBuilderClassName, ClassName.get(File.class));
return MethodSpec.methodBuilder("getDownloadOnlyRequest")
.addAnnotation(Override.class)
.addAnnotation(processorUtil.checkResult())
.addAnnotation(processorUtil.nonNull())
.returns(generatedRequestBuilderOfFile)
.addModifiers(Modifier.PROTECTED)
.addStatement(
"return new $T<>($T.class, $N).apply($N)",
generatedRequestBuilderClassName,
File.class,
"this",
"DOWNLOAD_ONLY_OPTIONS")
.build();
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/RequestManagerFactoryGenerator.java
================================================
package com.bumptech.glide.annotation.compiler;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeSpec;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
/**
* Generates an implementation of {@code
* com.bumptech.glide.manager.RequestManagerRetriever.RequestManagerFactory} that returns a
* generated {@code com.bumptech.glide.RequestManager} implementation.
*
* Generated {@code com.bumptech.glide.manager.RequestManagerRetriever.RequestManagerFactory}
* classes look like this:
*
*
*
* public class GeneratedRequestManagerFactory
* implements RequestManagerRetriever.RequestManagerFactory {
* {@literal @Override}
* public RequestManager build(Glide glide, Lifecycle lifecycle,
* RequestManagerTreeNode treeNode) {
* return new GeneratedRequestManager(glide, lifecycle, treeNode);
* }
* }
*
*
*/
final class RequestManagerFactoryGenerator {
private static final String GLIDE_QUALIFIED_NAME = "com.bumptech.glide.Glide";
private static final String LIFECYCLE_QUALIFIED_NAME = "com.bumptech.glide.manager.Lifecycle";
private static final String REQUEST_MANAGER_TREE_NODE_QUALIFIED_NAME =
"com.bumptech.glide.manager.RequestManagerTreeNode";
private static final String REQUEST_MANAGER_FACTORY_QUALIFIED_NAME =
"com.bumptech.glide.manager.RequestManagerRetriever.RequestManagerFactory";
private static final String REQUEST_MANAGER_QUALIFIED_NAME = "com.bumptech.glide.RequestManager";
private static final ClassName CONTEXT_CLASS_NAME = ClassName.get("android.content", "Context");
static final String GENERATED_REQUEST_MANAGER_FACTORY_PACKAGE_NAME = "com.bumptech.glide";
static final String GENERATED_REQUEST_MANAGER_FACTORY_SIMPLE_NAME =
"GeneratedRequestManagerFactory";
private final TypeElement glideType;
private final TypeElement lifecycleType;
private final TypeElement requestManagerTreeNodeType;
private final TypeElement requestManagerFactoryInterface;
private final ClassName requestManagerClassName;
private final ProcessorUtil processorUtil;
RequestManagerFactoryGenerator(ProcessingEnvironment processingEnv, ProcessorUtil processorUtil) {
this.processorUtil = processorUtil;
Elements elementUtils = processingEnv.getElementUtils();
glideType = elementUtils.getTypeElement(GLIDE_QUALIFIED_NAME);
lifecycleType = elementUtils.getTypeElement(LIFECYCLE_QUALIFIED_NAME);
requestManagerTreeNodeType =
elementUtils.getTypeElement(REQUEST_MANAGER_TREE_NODE_QUALIFIED_NAME);
requestManagerFactoryInterface =
elementUtils.getTypeElement(REQUEST_MANAGER_FACTORY_QUALIFIED_NAME);
TypeElement requestManagerType = elementUtils.getTypeElement(REQUEST_MANAGER_QUALIFIED_NAME);
requestManagerClassName = ClassName.get(requestManagerType);
}
TypeSpec generate(String generatedCodePackageName, TypeSpec generatedRequestManagerSpec) {
return TypeSpec.classBuilder(GENERATED_REQUEST_MANAGER_FACTORY_SIMPLE_NAME)
.addModifiers(Modifier.FINAL)
.addSuperinterface(ClassName.get(requestManagerFactoryInterface))
.addJavadoc("Generated code, do not modify\n")
.addMethod(
MethodSpec.methodBuilder("build")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addAnnotation(processorUtil.nonNull())
.returns(requestManagerClassName)
.addParameter(
ParameterSpec.builder(ClassName.get(glideType), "glide")
.addAnnotation(processorUtil.nonNull())
.build())
.addParameter(
ParameterSpec.builder(ClassName.get(lifecycleType), "lifecycle")
.addAnnotation(processorUtil.nonNull())
.build())
.addParameter(
ParameterSpec.builder(ClassName.get(requestManagerTreeNodeType), "treeNode")
.addAnnotation(processorUtil.nonNull())
.build())
.addParameter(
ParameterSpec.builder(CONTEXT_CLASS_NAME, "context")
.addAnnotation(processorUtil.nonNull())
.build())
.addStatement(
"return new $T(glide, lifecycle, treeNode, context)",
ClassName.get(generatedCodePackageName, generatedRequestManagerSpec.name))
.build())
.build();
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/RequestManagerGenerator.java
================================================
package com.bumptech.glide.annotation.compiler;
import com.bumptech.glide.annotation.GlideExtension;
import com.bumptech.glide.annotation.GlideType;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.MethodSpec.Builder;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
/**
* Generates an implementation of {@code com.bumptech.glide.RequestManager} that contains generated
* methods from {@link GlideExtension}s and {@link GlideType}.
*
* Generated {@code com.bumptech.glide.RequestManager} implementations look like this:
*
*
*
* public final class GeneratedRequestManager extends RequestManager {
* GeneratedRequestManager(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
* super(glide, lifecycle, treeNode);
* }
*
* public RequestBuilder asGif() {
* RequestBuilder requestBuilder = this.as(GifDrawable.class);
* GifOptions.asGif(requestBuilder);
* return requestBuilder;
* }
* }
*
*
*/
final class RequestManagerGenerator {
private static final String GLIDE_QUALIFIED_NAME = "com.bumptech.glide.Glide";
private static final String REQUEST_MANAGER_QUALIFIED_NAME = "com.bumptech.glide.RequestManager";
private static final String LIFECYCLE_QUALIFIED_NAME = "com.bumptech.glide.manager.Lifecycle";
private static final String REQUEST_MANAGER_TREE_NODE_QUALIFIED_NAME =
"com.bumptech.glide.manager.RequestManagerTreeNode";
private static final ClassName CONTEXT_CLASS_NAME = ClassName.get("android.content", "Context");
private static final String GENERATED_REQUEST_MANAGER_SIMPLE_NAME = "GlideRequests";
private ProcessingEnvironment processingEnv;
private final ProcessorUtil processorUtil;
private final ClassName requestManagerClassName;
private final TypeElement lifecycleType;
private final TypeElement requestManagerTreeNodeType;
private final TypeElement glideType;
private final TypeElement requestManagerType;
private final TypeElement requestBuilderType;
private ClassName generatedRequestBuilderClassName;
RequestManagerGenerator(ProcessingEnvironment processingEnv, ProcessorUtil processorUtil) {
this.processingEnv = processingEnv;
this.processorUtil = processorUtil;
Elements elementUtils = processingEnv.getElementUtils();
requestManagerType = elementUtils.getTypeElement(REQUEST_MANAGER_QUALIFIED_NAME);
requestManagerClassName = ClassName.get(requestManagerType);
lifecycleType = elementUtils.getTypeElement(LIFECYCLE_QUALIFIED_NAME);
requestManagerTreeNodeType =
elementUtils.getTypeElement(REQUEST_MANAGER_TREE_NODE_QUALIFIED_NAME);
requestBuilderType =
elementUtils.getTypeElement(RequestBuilderGenerator.REQUEST_BUILDER_QUALIFIED_NAME);
glideType = elementUtils.getTypeElement(GLIDE_QUALIFIED_NAME);
}
TypeSpec generate(
String generatedCodePackageName,
@Nullable TypeSpec requestOptions,
TypeSpec requestBuilder,
Set glideExtensions) {
generatedRequestBuilderClassName = ClassName.get(generatedCodePackageName, requestBuilder.name);
return TypeSpec.classBuilder(GENERATED_REQUEST_MANAGER_SIMPLE_NAME)
.superclass(requestManagerClassName)
.addJavadoc(
"Includes all additions from methods in {@link $T}s\n"
+ "annotated with {@link $T}\n"
+ "\n"
+ "Generated code, do not modify\n",
GlideExtension.class,
GlideType.class)
.addAnnotation(
AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "deprecation")
.build())
.addModifiers(Modifier.PUBLIC)
.addMethod(generateAsMethod(generatedCodePackageName, requestBuilder))
.addMethod(generateCallSuperConstructor())
.addMethods(generateExtensionRequestManagerMethods(glideExtensions))
.addMethods(generateRequestManagerRequestManagerMethodOverrides(generatedCodePackageName))
.addMethods(generateRequestManagerRequestBuilderMethodOverrides())
.addMethods(
FluentIterable.from(
Collections.singletonList(
generateOverrideSetRequestOptions(
generatedCodePackageName, requestOptions)))
.filter(Predicates.notNull()))
.build();
}
private MethodSpec generateCallSuperConstructor() {
return MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(
ParameterSpec.builder(ClassName.get(glideType), "glide")
.addAnnotation(processorUtil.nonNull())
.build())
.addParameter(
ParameterSpec.builder(ClassName.get(lifecycleType), "lifecycle")
.addAnnotation(processorUtil.nonNull())
.build())
.addParameter(
ParameterSpec.builder(ClassName.get(requestManagerTreeNodeType), "treeNode")
.addAnnotation(processorUtil.nonNull())
.build())
.addParameter(
ParameterSpec.builder(CONTEXT_CLASS_NAME, "context")
.addAnnotation(processorUtil.nonNull())
.build())
.addStatement("super(glide, lifecycle, treeNode, context)")
.build();
}
private MethodSpec generateAsMethod(String generatedCodePackageName, TypeSpec requestBuilder) {
TypeVariableName resourceType = TypeVariableName.get("ResourceType");
ParameterizedTypeName classOfResouceType =
ParameterizedTypeName.get(ClassName.get(Class.class), resourceType);
ClassName generatedRequestBuilderClassName =
ClassName.get(generatedCodePackageName, requestBuilder.name);
ParameterizedTypeName requestBuilderOfResourceType =
ParameterizedTypeName.get(generatedRequestBuilderClassName, resourceType);
return MethodSpec.methodBuilder("as")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addAnnotation(processorUtil.checkResult())
.addAnnotation(processorUtil.nonNull())
.addTypeVariable(TypeVariableName.get("ResourceType"))
.returns(requestBuilderOfResourceType)
.addParameter(
classOfResouceType.annotated(AnnotationSpec.builder(processorUtil.nonNull()).build()),
"resourceClass")
.addStatement(
"return new $T<>(glide, this, resourceClass, context)",
this.generatedRequestBuilderClassName)
.build();
}
/** Generates the list of overrides of methods that return {@code RequestManager}. */
private List generateRequestManagerRequestManagerMethodOverrides(
final String generatedPackageName) {
return FluentIterable.from(
processorUtil.findInstanceMethodsReturning(requestManagerType, requestManagerType))
.transform(
new Function() {
@Override
public MethodSpec apply(@Nullable ExecutableElement input) {
return generateRequestManagerRequestManagerMethodOverride(
generatedPackageName, input);
}
})
.toList();
}
private MethodSpec generateRequestManagerRequestManagerMethodOverride(
String generatedPackageName, ExecutableElement method) {
ClassName generatedRequestManagerName =
ClassName.get(generatedPackageName, GENERATED_REQUEST_MANAGER_SIMPLE_NAME);
Builder returns =
processorUtil
.overriding(method)
.addAnnotation(processorUtil.nonNull())
.returns(generatedRequestManagerName);
return returns
.addCode(
ProcessorUtil.generateCastingSuperCall(generatedRequestManagerName, returns.build()))
.build();
}
/** Generates the list of overrides of methods that return {@code RequestBuilder}. */
private List generateRequestManagerRequestBuilderMethodOverrides() {
// Without the erasure, this is a RequestBuilder. A RequestBuilder is not assignable to a
// RequestBuilder. After type erasure this is a RequestBuilder. A RequestBuilder is
// assignable to the raw RequestBuilder.
TypeMirror rawRequestBuilder =
processingEnv.getTypeUtils().erasure(requestBuilderType.asType());
return FluentIterable.from(
processorUtil.findInstanceMethodsReturning(requestManagerType, rawRequestBuilder))
.filter(
new Predicate() {
@Override
public boolean apply(ExecutableElement input) {
// Skip the as(Class) method.
return !input.getSimpleName().toString().equals("as");
}
})
.transform(
new Function() {
@Override
public MethodSpec apply(ExecutableElement input) {
return generateRequestManagerRequestBuilderMethodOverride(input);
}
})
.toList();
}
/**
* Generates overrides of existing RequestManager methods so that they return our generated
* RequestBuilder subtype.
*/
private MethodSpec generateRequestManagerRequestBuilderMethodOverride(
ExecutableElement methodToOverride) {
// We've already verified that this method returns a RequestBuilder and RequestBuilders have
// exactly one type argument, so this is safe unless those assumptions change.
TypeMirror typeArgument =
((DeclaredType) methodToOverride.getReturnType()).getTypeArguments().get(0);
ParameterizedTypeName generatedRequestBuilderOfType =
ParameterizedTypeName.get(generatedRequestBuilderClassName, ClassName.get(typeArgument));
MethodSpec.Builder builder =
processorUtil.overriding(methodToOverride).returns(generatedRequestBuilderOfType);
builder.addCode(
ProcessorUtil.generateCastingSuperCall(generatedRequestBuilderOfType, builder.build()));
for (AnnotationMirror mirror : methodToOverride.getAnnotationMirrors()) {
builder.addAnnotation(AnnotationSpec.get(mirror));
}
return builder.build();
}
private List generateExtensionRequestManagerMethods(Set glideExtensions) {
List requestManagerExtensionMethods =
processorUtil.findAnnotatedElementsInClasses(glideExtensions, GlideType.class);
return Lists.transform(
requestManagerExtensionMethods,
new Function() {
@Override
public MethodSpec apply(ExecutableElement input) {
return generateAdditionalRequestManagerMethod(input);
}
});
}
// Generates methods added to RequestManager via GlideExtensions.
private MethodSpec generateAdditionalRequestManagerMethod(ExecutableElement extensionMethod) {
if (extensionMethod.getReturnType().getKind() == TypeKind.VOID) {
return generateAdditionalRequestManagerMethodLegacy(extensionMethod);
} else {
return generateAdditionalRequestManagerMethodNew(extensionMethod);
}
}
private MethodSpec generateAdditionalRequestManagerMethodLegacy(
ExecutableElement extensionMethod) {
String returnType =
processorUtil
.findClassValuesFromAnnotationOnClassAsNames(extensionMethod, GlideType.class)
.iterator()
.next();
ClassName returnTypeClassName = ClassName.bestGuess(returnType);
ParameterizedTypeName parameterizedTypeName =
ParameterizedTypeName.get(generatedRequestBuilderClassName, returnTypeClassName);
return MethodSpec.methodBuilder(extensionMethod.getSimpleName().toString())
.addModifiers(Modifier.PUBLIC)
.returns(parameterizedTypeName)
.addJavadoc(processorUtil.generateSeeMethodJavadoc(extensionMethod))
.addAnnotation(processorUtil.nonNull())
.addAnnotation(processorUtil.checkResult())
.addStatement(
"$T requestBuilder = this.as($T.class)", parameterizedTypeName, returnTypeClassName)
.addStatement(
"$T.$N(requestBuilder)",
extensionMethod.getEnclosingElement(),
extensionMethod.getSimpleName())
.addStatement("return requestBuilder")
.build();
}
private MethodSpec generateAdditionalRequestManagerMethodNew(ExecutableElement extensionMethod) {
String returnType =
processorUtil
.findClassValuesFromAnnotationOnClassAsNames(extensionMethod, GlideType.class)
.iterator()
.next();
ClassName returnTypeClassName = ClassName.bestGuess(returnType);
ParameterizedTypeName parameterizedTypeName =
ParameterizedTypeName.get(generatedRequestBuilderClassName, returnTypeClassName);
return MethodSpec.methodBuilder(extensionMethod.getSimpleName().toString())
.addModifiers(Modifier.PUBLIC)
.returns(parameterizedTypeName)
.addJavadoc(processorUtil.generateSeeMethodJavadoc(extensionMethod))
.addAnnotation(processorUtil.nonNull())
.addAnnotation(processorUtil.checkResult())
.addStatement(
"return ($T) $T.$N(this.as($T.class))",
parameterizedTypeName,
extensionMethod.getEnclosingElement(),
extensionMethod.getSimpleName(),
returnTypeClassName)
.build();
}
/**
* The {@code RequestOptions} subclass should always be our generated subclass type to avoid
* inadvertent errors where a different subclass is applied that accidentally wipes out some logic
* in overidden methods in our generated subclass.
*/
@Nullable
private MethodSpec generateOverrideSetRequestOptions(
String generatedCodePackageName, @Nullable TypeSpec generatedRequestOptions) {
if (generatedRequestOptions == null) {
return null;
}
Elements elementUtils = processingEnv.getElementUtils();
TypeElement requestOptionsType =
elementUtils.getTypeElement(RequestOptionsGenerator.REQUEST_OPTIONS_QUALIFIED_NAME);
// This class may have just been generated and therefore may not be found if we try to obtain
// it via Elements, so use just the String version instead.
String generatedRequestOptionsQualifiedName =
generatedCodePackageName + "." + generatedRequestOptions.name;
String methodName = "setRequestOptions";
String parameterName = "toSet";
return MethodSpec.methodBuilder(methodName)
.addAnnotation(Override.class)
.addModifiers(Modifier.PROTECTED)
.addParameter(
ParameterSpec.builder(ClassName.get(requestOptionsType), parameterName)
.addAnnotation(processorUtil.nonNull())
.build())
.beginControlFlow(
"if ($N instanceof $L)", parameterName, generatedRequestOptionsQualifiedName)
.addStatement("super.$N($N)", methodName, parameterName)
.nextControlFlow("else")
.addStatement(
"super.setRequestOptions(new $L().apply($N))",
generatedRequestOptionsQualifiedName,
parameterName)
.endControlFlow()
.build();
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/RequestOptionsExtensionGenerator.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.GlideOption.OVERRIDE_EXTEND;
import com.bumptech.glide.annotation.GlideOption;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
/**
* Generates method overrides for classes that want to mix in {@link GlideOption} annotated methods
* in Glide extensions.
*/
final class RequestOptionsExtensionGenerator {
private TypeName containingClassName;
private ProcessorUtil processorUtil;
RequestOptionsExtensionGenerator(TypeName containingClassName, ProcessorUtil processorUtil) {
this.containingClassName = containingClassName;
this.processorUtil = processorUtil;
}
/**
* Returns the set of {@link GlideOption} annotated methods in the classes that correspond to the
* given extension class names.
*/
List getRequestOptionExtensionMethods(Set glideExtensionClassNames) {
return processorUtil.findAnnotatedElementsInClasses(
glideExtensionClassNames, GlideOption.class);
}
/**
* Returns a list containing an override {@link MethodSpec} for all {@link GlideOption} annotated
* methods in the classes that correspond to the given extension class names.
*/
List generateInstanceMethodsForExtensions(Set glideExtensionClassNames) {
List requestOptionExtensionMethods =
getRequestOptionExtensionMethods(glideExtensionClassNames);
List result = new ArrayList<>(requestOptionExtensionMethods.size());
for (ExecutableElement requestOptionsExtensionMethod : requestOptionExtensionMethods) {
result.add(generateMethodsForRequestOptionsExtension(requestOptionsExtensionMethod));
}
return result;
}
private MethodSpec generateMethodsForRequestOptionsExtension(ExecutableElement element) {
// Assert for legacy versions
if (element.getReturnType().getKind() == TypeKind.VOID) {
throw new IllegalArgumentException(
"The "
+ element.getSimpleName()
+ " method annotated with @GlideOption in the "
+ element.getEnclosingElement().getSimpleName()
+ " @GlideExtension is using a legacy"
+ " format that is no longer supported. Please change your method definition so that"
+ " your @GlideModule annotated methods return BaseRequestOptions> objects instead"
+ " of null.");
}
int overrideType = processorUtil.getOverrideType(element);
String methodName = element.getSimpleName().toString();
MethodSpec.Builder builder =
MethodSpec.methodBuilder(methodName)
.addModifiers(Modifier.PUBLIC)
.addJavadoc(processorUtil.generateSeeMethodJavadoc(element))
.varargs(element.isVarArgs())
.returns(containingClassName)
.addAnnotation(
AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "unchecked")
.build());
// The 0th element is expected to be a RequestOptions object.
List extends VariableElement> paramElements =
element.getParameters().subList(1, element.getParameters().size());
List parameters = processorUtil.getParameters(paramElements);
builder.addParameters(parameters);
String extensionRequestOptionsArgument;
if (overrideType == OVERRIDE_EXTEND) {
builder
.addJavadoc(
processorUtil.generateSeeMethodJavadoc(
containingClassName, methodName, paramElements))
.addAnnotation(Override.class);
List methodArgs = new ArrayList<>();
methodArgs.add(element.getSimpleName().toString());
StringBuilder methodLiterals = new StringBuilder();
if (!parameters.isEmpty()) {
for (ParameterSpec parameter : parameters) {
methodLiterals.append("$L, ");
methodArgs.add(parameter.name);
}
methodLiterals =
new StringBuilder(methodLiterals.substring(0, methodLiterals.length() - 2));
}
extensionRequestOptionsArgument =
CodeBlock.builder()
.add("super.$N(" + methodLiterals + ")", methodArgs.toArray(new Object[0]))
.build()
.toString();
} else {
extensionRequestOptionsArgument = "this";
}
List args = new ArrayList<>();
StringBuilder code = new StringBuilder("return ($T) $T.$L($L, ");
args.add(containingClassName);
args.add(ClassName.get(element.getEnclosingElement().asType()));
args.add(element.getSimpleName().toString());
args.add(extensionRequestOptionsArgument);
if (!parameters.isEmpty()) {
for (ParameterSpec parameter : parameters) {
code.append("$L, ");
args.add(parameter.name);
}
}
code = new StringBuilder(code.substring(0, code.length() - 2));
code.append(")");
builder.addStatement(code.toString(), args.toArray(new Object[0]));
builder.addAnnotation(processorUtil.checkResult()).addAnnotation(processorUtil.nonNull());
return builder.build();
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/RequestOptionsGenerator.java
================================================
package com.bumptech.glide.annotation.compiler;
import com.bumptech.glide.annotation.GlideExtension;
import com.bumptech.glide.annotation.GlideOption;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.CodeBlock.Builder;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
/**
* Generates a new implementation of {@code com.bumptech.glide.request.RequestOptions} containing
* static versions of methods included in the base class and static and instance versions of all
* methods annotated with {@link GlideOption} in classes annotated with {@link GlideExtension}.
*
* The generated class looks something like this:
*
*
*
* public final class GlideOptions extends com.bumptech.glide.request.RequestOptions {
*
* public static com.google.android.apps.photos.glide.GlideOptions signatureOf(
* com.bumptech.glide.load.Key arg0) {
* return new com.google.android.apps.photos.glide.GlideOptions()
* .apply(com.bumptech.glide.request.RequestOptions.signatureOf(arg0));
* }
*
* ... // The rest of the static versions of methods from RequestOptions go here.
*
* // Now on to methods generated from an extension:
* public com.bumptech.glide.GlideOptions dontAnimate() {
* com.bumptech.glide.integration.gifdecoder.GifOptions.dontAnimate(this);
* return this;
* }
*
* public static com.bumptech.glide.GlideOptions noAnimation() {
* return new com.bumptech.glide.GlideOptions().dontAnimate();
* }
* }
*
*
*/
final class RequestOptionsGenerator {
private static final String GENERATED_REQUEST_OPTIONS_SIMPLE_NAME = "GlideOptions";
static final String REQUEST_OPTIONS_PACKAGE_NAME = "com.bumptech.glide.request";
private static final String REQUEST_OPTIONS_SIMPLE_NAME = "RequestOptions";
static final String REQUEST_OPTIONS_QUALIFIED_NAME =
REQUEST_OPTIONS_PACKAGE_NAME + "." + REQUEST_OPTIONS_SIMPLE_NAME;
static final String BASE_REQUEST_OPTIONS_SIMPLE_NAME = "BaseRequestOptions";
static final String BASE_REQUEST_OPTIONS_QUALIFIED_NAME =
REQUEST_OPTIONS_PACKAGE_NAME + "." + BASE_REQUEST_OPTIONS_SIMPLE_NAME;
private int nextFieldId;
private final ClassName requestOptionsName;
private final TypeElement requestOptionsType;
private final ProcessorUtil processorUtil;
private final RequestOptionsOverrideGenerator requestOptionsOverrideGenerator;
private ClassName glideOptionsName;
RequestOptionsGenerator(
ProcessingEnvironment processingEnvironment, ProcessorUtil processorUtil) {
this.processorUtil = processorUtil;
requestOptionsName = ClassName.get(REQUEST_OPTIONS_PACKAGE_NAME, REQUEST_OPTIONS_SIMPLE_NAME);
requestOptionsType =
processingEnvironment.getElementUtils().getTypeElement(REQUEST_OPTIONS_QUALIFIED_NAME);
requestOptionsOverrideGenerator =
new RequestOptionsOverrideGenerator(processingEnvironment, processorUtil);
}
TypeSpec generate(String generatedCodePackageName, Set glideExtensionClassNames) {
glideOptionsName =
ClassName.get(generatedCodePackageName, GENERATED_REQUEST_OPTIONS_SIMPLE_NAME);
RequestOptionsExtensionGenerator requestOptionsExtensionGenerator =
new RequestOptionsExtensionGenerator(glideOptionsName, processorUtil);
List instanceMethodsForExtensions =
FluentIterable.from(
requestOptionsExtensionGenerator.generateInstanceMethodsForExtensions(
glideExtensionClassNames))
.transform(
new Function() {
@Override
public MethodAndStaticVar apply(MethodSpec input) {
return new MethodAndStaticVar(input);
}
})
.toList();
List staticMethodsForExtensions =
FluentIterable.from(
requestOptionsExtensionGenerator.getRequestOptionExtensionMethods(
glideExtensionClassNames))
.filter(
new Predicate() {
@Override
public boolean apply(ExecutableElement input) {
return !skipStaticMethod(input);
}
})
.transform(
new Function() {
@Override
public MethodAndStaticVar apply(ExecutableElement input) {
return generateStaticMethodEquivalentForExtensionMethod(input);
}
})
.toList();
List methodsForExtensions = new ArrayList<>();
methodsForExtensions.addAll(instanceMethodsForExtensions);
methodsForExtensions.addAll(staticMethodsForExtensions);
Set extensionMethodSignatures =
ImmutableSet.copyOf(
Iterables.transform(
methodsForExtensions,
new Function() {
@Override
public MethodSignature apply(MethodAndStaticVar f) {
return new MethodSignature(f.method);
}
}));
List staticOverrides = generateStaticMethodOverridesForRequestOptions();
List instanceOverrides =
requestOptionsOverrideGenerator.generateInstanceMethodOverridesForRequestOptions(
glideOptionsName);
List allMethodsAndStaticVars = new ArrayList<>();
for (MethodAndStaticVar item : staticOverrides) {
if (extensionMethodSignatures.contains(new MethodSignature(item.method))) {
continue;
}
allMethodsAndStaticVars.add(item);
}
for (MethodSpec methodSpec : instanceOverrides) {
if (extensionMethodSignatures.contains(new MethodSignature(methodSpec))) {
continue;
}
allMethodsAndStaticVars.add(new MethodAndStaticVar(methodSpec));
}
allMethodsAndStaticVars.addAll(methodsForExtensions);
TypeSpec.Builder classBuilder =
TypeSpec.classBuilder(GENERATED_REQUEST_OPTIONS_SIMPLE_NAME)
.addAnnotation(
AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "deprecation")
.build())
.addJavadoc(generateClassJavadoc(glideExtensionClassNames))
.addModifiers(Modifier.FINAL)
.addModifiers(Modifier.PUBLIC)
.addSuperinterface(Cloneable.class)
.superclass(requestOptionsName);
for (MethodAndStaticVar methodAndStaticVar : allMethodsAndStaticVars) {
if (methodAndStaticVar.method != null) {
classBuilder.addMethod(methodAndStaticVar.method);
}
if (methodAndStaticVar.staticField != null) {
classBuilder.addField(methodAndStaticVar.staticField);
}
}
return classBuilder.build();
}
private CodeBlock generateClassJavadoc(Set glideExtensionClassNames) {
Builder builder =
CodeBlock.builder()
.add(
"Automatically generated from {@link $T} annotated classes.\n",
GlideExtension.class)
.add("\n")
.add("@see $T\n", requestOptionsName);
for (String glideExtensionClass : glideExtensionClassNames) {
builder.add("@see $T\n", ClassName.bestGuess(glideExtensionClass));
}
return builder.build();
}
private List generateStaticMethodOverridesForRequestOptions() {
List staticMethodsThatReturnRequestOptions =
processorUtil.findStaticMethodsReturning(requestOptionsType, requestOptionsType);
List staticMethods = new ArrayList<>();
for (ExecutableElement element : staticMethodsThatReturnRequestOptions) {
if (element.getAnnotation(Deprecated.class) != null) {
continue;
}
staticMethods.add(generateStaticMethodEquivalentForRequestOptionsStaticMethod(element));
}
return staticMethods;
}
/**
* This method is a bit of a hack, but it lets us tie the static version of a method with the
* instance version. In turn that lets us call the instance versions on the generated subclass,
* instead of just delegating to the RequestOptions static methods. Using the instance methods on
* the generated subclass allows our static methods to properly call code that overrides an
* existing method in RequestOptions.
*
* The string names here just map between the static methods in {@code
* com.bumptech.glide.request.RequestOptions} and the instance methods they call.
*/
private static String getInstanceMethodNameFromStaticMethodName(String staticMethodName) {
String equivalentInstanceMethodName;
if ("bitmapTransform".equals(staticMethodName)) {
equivalentInstanceMethodName = "transform";
} else if ("decodeTypeOf".equals(staticMethodName)) {
equivalentInstanceMethodName = "decode";
} else if (staticMethodName.endsWith("Transform")) {
equivalentInstanceMethodName = staticMethodName.substring(0, staticMethodName.length() - 9);
} else if (staticMethodName.endsWith("Of")) {
equivalentInstanceMethodName = staticMethodName.substring(0, staticMethodName.length() - 2);
} else if ("noTransformation".equals(staticMethodName)) {
equivalentInstanceMethodName = "dontTransform";
} else if ("noAnimation".equals(staticMethodName)) {
equivalentInstanceMethodName = "dontAnimate";
} else if (staticMethodName.equals("option")) {
equivalentInstanceMethodName = "set";
} else {
throw new IllegalArgumentException("Unrecognized static method name: " + staticMethodName);
}
return equivalentInstanceMethodName;
}
private MethodAndStaticVar generateStaticMethodEquivalentForRequestOptionsStaticMethod(
ExecutableElement staticMethod) {
boolean memoize = memoizeStaticMethodFromArguments(staticMethod);
String staticMethodName = staticMethod.getSimpleName().toString();
String equivalentInstanceMethodName =
getInstanceMethodNameFromStaticMethodName(staticMethodName);
MethodSpec.Builder methodSpecBuilder =
MethodSpec.methodBuilder(staticMethodName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addJavadoc(processorUtil.generateSeeMethodJavadoc(staticMethod))
.returns(glideOptionsName);
StringBuilder createNewOptionAndCall =
createNewOptionAndCall(
memoize, methodSpecBuilder, "new $T().$N(", processorUtil.getParameters(staticMethod));
FieldSpec requiredStaticField = null;
if (memoize) {
// Generates code that looks like:
// if (GlideOptions. == null) {
// GlideOptions. = new GlideOptions().().autoClone()
// }
// Mix in an incrementing unique id to handle method overloading.
String staticVariableName = staticMethodName + nextFieldId++;
requiredStaticField =
FieldSpec.builder(glideOptionsName, staticVariableName)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
.build();
methodSpecBuilder
.beginControlFlow("if ($T.$N == null)", glideOptionsName, staticVariableName)
.addStatement(
"$T.$N =\n" + createNewOptionAndCall + ".$N",
glideOptionsName,
staticVariableName,
glideOptionsName,
equivalentInstanceMethodName,
"autoClone()")
.endControlFlow()
.addStatement("return $T.$N", glideOptionsName, staticVariableName);
} else {
// Generates code that looks like:
// return new GlideOptions().()
methodSpecBuilder.addStatement(
"return " + createNewOptionAndCall, glideOptionsName, equivalentInstanceMethodName);
}
List extends TypeParameterElement> typeParameters = staticMethod.getTypeParameters();
for (TypeParameterElement typeParameterElement : typeParameters) {
methodSpecBuilder.addTypeVariable(
TypeVariableName.get(typeParameterElement.getSimpleName().toString()));
}
methodSpecBuilder
.addAnnotation(processorUtil.checkResult())
.addAnnotation(processorUtil.nonNull());
return new MethodAndStaticVar(methodSpecBuilder.build(), requiredStaticField);
}
@SuppressWarnings("checkstyle:UnnecessaryParentheses") // Readability
private static boolean memoizeStaticMethodFromArguments(ExecutableElement staticMethod) {
return staticMethod.getParameters().isEmpty()
|| (staticMethod.getParameters().size() == 1
&& staticMethod
.getParameters()
.get(0)
.getSimpleName()
.toString()
.equals("android.content.Context"));
}
private StringBuilder createNewOptionAndCall(
boolean memoize,
MethodSpec.Builder methodSpecBuilder,
String start,
List specs) {
StringBuilder createNewOptionAndCall = new StringBuilder(start);
if (!specs.isEmpty()) {
methodSpecBuilder.addParameters(specs);
for (ParameterSpec parameter : specs) {
createNewOptionAndCall.append(parameter.name);
// use the Application Context to avoid memory leaks.
if (memoize && isAndroidContext(parameter)) {
createNewOptionAndCall.append(".getApplicationContext()");
}
createNewOptionAndCall.append(", ");
}
createNewOptionAndCall =
new StringBuilder(
createNewOptionAndCall.substring(0, createNewOptionAndCall.length() - 2));
}
createNewOptionAndCall.append(")");
return createNewOptionAndCall;
}
private boolean isAndroidContext(ParameterSpec parameter) {
return parameter.type.toString().equals("android.content.Context");
}
private MethodAndStaticVar generateStaticMethodEquivalentForExtensionMethod(
ExecutableElement instanceMethod) {
String staticMethodName = getStaticMethodName(instanceMethod);
String instanceMethodName = instanceMethod.getSimpleName().toString();
if (Strings.isNullOrEmpty(staticMethodName)) {
if (instanceMethodName.startsWith("dont")) {
staticMethodName = "no" + instanceMethodName.replace("dont", "");
} else {
staticMethodName = instanceMethodName + "Of";
}
}
boolean memoize = memoizeStaticMethodFromAnnotation(instanceMethod);
//noinspection ResultOfMethodCallIgnored
Preconditions.checkNotNull(staticMethodName);
MethodSpec.Builder methodSpecBuilder =
MethodSpec.methodBuilder(staticMethodName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addJavadoc(processorUtil.generateSeeMethodJavadoc(instanceMethod))
.varargs(instanceMethod.isVarArgs())
.returns(glideOptionsName);
List extends VariableElement> parameters = instanceMethod.getParameters();
// Always remove the first parameter because it's always RequestOptions in extensions. The
// actual method we want to generate will pass the RequestOptions in to the extension method,
// but should not itself require a RequestOptions object to be passed in.
if (parameters.isEmpty()) {
throw new IllegalArgumentException("Expected non-empty parameters for: " + instanceMethod);
}
// Remove is not supported.
parameters = parameters.subList(1, parameters.size());
StringBuilder createNewOptionAndCall =
createNewOptionAndCall(
memoize, methodSpecBuilder, "new $T().$L(", processorUtil.getParameters(parameters));
FieldSpec requiredStaticField = null;
if (memoize) {
// Generates code that looks like:
// if (GlideOptions. == null) {
// GlideOptions. = new GlideOptions().().autoClone()
// }
// Mix in an incrementing unique id to handle method overloading.
String staticVariableName = staticMethodName + nextFieldId++;
requiredStaticField =
FieldSpec.builder(glideOptionsName, staticVariableName)
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
.build();
methodSpecBuilder
.beginControlFlow("if ($T.$N == null)", glideOptionsName, staticVariableName)
.addStatement(
"$T.$N =\n" + createNewOptionAndCall + ".$N",
glideOptionsName,
staticVariableName,
glideOptionsName,
instanceMethodName,
"autoClone()")
.endControlFlow()
.addStatement("return $T.$N", glideOptionsName, staticVariableName);
} else {
// Generates code that looks like:
// return new GlideOptions().()
methodSpecBuilder.addStatement(
"return " + createNewOptionAndCall, glideOptionsName, instanceMethodName);
}
List extends TypeParameterElement> typeParameters = instanceMethod.getTypeParameters();
for (TypeParameterElement typeParameterElement : typeParameters) {
methodSpecBuilder.addTypeVariable(
TypeVariableName.get(typeParameterElement.getSimpleName().toString()));
}
methodSpecBuilder.addAnnotation(processorUtil.checkResult());
return new MethodAndStaticVar(methodSpecBuilder.build(), requiredStaticField);
}
@Nullable
private static String getStaticMethodName(ExecutableElement element) {
GlideOption glideOption = element.getAnnotation(GlideOption.class);
String result = glideOption != null ? glideOption.staticMethodName() : null;
return Strings.emptyToNull(result);
}
private static boolean memoizeStaticMethodFromAnnotation(ExecutableElement element) {
GlideOption glideOption = element.getAnnotation(GlideOption.class);
return glideOption != null && glideOption.memoizeStaticMethod();
}
private static boolean skipStaticMethod(ExecutableElement element) {
GlideOption glideOption = element.getAnnotation(GlideOption.class);
return glideOption != null && glideOption.skipStaticMethod();
}
private static final class MethodAndStaticVar {
@Nullable final MethodSpec method;
@Nullable final FieldSpec staticField;
MethodAndStaticVar(@Nullable MethodSpec method) {
this(method, null /*staticField*/);
}
MethodAndStaticVar(@Nullable MethodSpec method, @Nullable FieldSpec staticField) {
this.method = method;
this.staticField = staticField;
}
}
private static final class MethodSignature {
private final TypeName returnType;
private final List parameterTypes;
private final boolean isStatic;
private final String name;
MethodSignature(MethodSpec spec) {
name = spec.name;
isStatic = spec.modifiers.contains(Modifier.STATIC);
returnType = spec.returnType;
parameterTypes =
Lists.transform(
spec.parameters,
new Function() {
@Nullable
@Override
public TypeName apply(ParameterSpec parameterSpec) {
return parameterSpec.type;
}
});
}
@Override
public boolean equals(Object o) {
if (o instanceof MethodSignature) {
MethodSignature other = (MethodSignature) o;
return name.equals(other.name)
&& returnType.equals(other.returnType)
&& parameterTypes.equals(other.parameterTypes)
&& isStatic == other.isStatic;
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(name, returnType, parameterTypes, isStatic);
}
}
}
================================================
FILE: annotation/compiler/src/main/java/com/bumptech/glide/annotation/compiler/RequestOptionsOverrideGenerator.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.RequestOptionsGenerator.BASE_REQUEST_OPTIONS_QUALIFIED_NAME;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
/**
* Generates overrides for BaseRequestOptions methods so that subclasses' methods return the
* subclass type, not just BaseRequestOptions.
*/
final class RequestOptionsOverrideGenerator {
private final TypeElement baseRequestOptionsType;
private ProcessorUtil processorUtil;
RequestOptionsOverrideGenerator(
ProcessingEnvironment processingEnv, ProcessorUtil processorUtil) {
this.processorUtil = processorUtil;
baseRequestOptionsType =
processingEnv.getElementUtils().getTypeElement(BASE_REQUEST_OPTIONS_QUALIFIED_NAME);
}
List generateInstanceMethodOverridesForRequestOptions(TypeName typeToOverrideIn) {
return generateInstanceMethodOverridesForRequestOptions(
typeToOverrideIn, Collections.emptySet());
}
List generateInstanceMethodOverridesForRequestOptions(
final TypeName typeToOverrideIn, final Set excludedMethods) {
return FluentIterable.from(
processorUtil.findInstanceMethodsReturning(
baseRequestOptionsType, baseRequestOptionsType))
.filter(
new Predicate() {
@Override
public boolean apply(ExecutableElement input) {
return !excludedMethods.contains(input.getSimpleName().toString());
}
})
.transform(
new Function() {
@Override
public MethodSpec apply(ExecutableElement input) {
return generateRequestOptionOverride(typeToOverrideIn, input);
}
})
.toList();
}
private MethodSpec generateRequestOptionOverride(
TypeName typeToOverrideIn, ExecutableElement methodToOverride) {
MethodSpec.Builder result =
processorUtil.overriding(methodToOverride).returns(typeToOverrideIn);
result.addCode(
CodeBlock.builder()
.add("return ($T) super.$N(", typeToOverrideIn, methodToOverride.getSimpleName())
.add(
FluentIterable.from(result.build().parameters)
.transform(
new Function() {
@Override
public String apply(ParameterSpec input) {
return input.name;
}
})
.join(Joiner.on(", ")))
.add(");\n")
.build());
if (methodToOverride.getSimpleName().toString().contains("transform")
&& methodToOverride.isVarArgs()) {
result
.addModifiers(Modifier.FINAL)
.addAnnotation(SafeVarargs.class)
.addAnnotation(
AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "varargs")
.build());
}
for (AnnotationMirror mirror : methodToOverride.getAnnotationMirrors()) {
result.addAnnotation(AnnotationSpec.get(mirror));
}
return result.build();
}
}
================================================
FILE: annotation/compiler/src/main/resources/META-INF/gradle/incremental.annotation.processors
================================================
com.bumptech.glide.annotation.compiler.GlideAnnotationProcessor,aggregating
================================================
FILE: annotation/compiler/test/.gitignore
================================================
/build
================================================
FILE: annotation/compiler/test/build.gradle
================================================
apply plugin: 'com.android.library'
android {
sourceSets {
test {
resources {
// *.java is excluded by default...
setExcludes([])
}
// TODO: Re-enable these tests after fixing import orders.
java {
exclude "**/AppGlideModuleWithExcludesTest.java"
exclude "**/AppGlideModuleWithLibraryInPackageTest.java"
exclude "**/AppGlideModuleWithMultipleExcludesTest.java"
exclude "**/EmptyAppAndLibraryGlideModulesTest.java"
exclude "**/GlideExtensionWithOptionTest.java"
exclude "**/GlideExtensionWithTypeTest.java"
exclude "**/GlideExtensionWithTypeTest.java"
}
}
}
}
afterEvaluate {
lint.enabled = false
compileReleaseJavaWithJavac.enabled = false
}
android {
namespace 'com.bumptech.glide.annotation.compiler.test'
compileSdk libs.versions.compile.sdk.version.get()
defaultConfig {
minSdk libs.versions.min.sdk.version.get() as int
targetSdk libs.versions.target.sdk.version.get() as int
versionName VERSION_NAME as String
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
testOptions {
unitTests {
all { Test testTask ->
testTask.maxParallelForks = 2
}
}
}
}
// This special test only submodule exists because adding the :glide dependency seems to break
// the annotation processor dependency chain for the internal sample apps. It's also somewhat
// easier to parse as a separate module given the existing complexity here and in the compiler
// build.gradle file.
dependencies {
testImplementation project(':glide')
testImplementation project(':annotation:compiler')
testImplementation libs.junit
testImplementation libs.javapoet
testImplementation libs.findbugs.jsr305
// Using 0.10 of compile-testing is required for Android Studio to function, but not for the
// gradle build. Not yet clear why, but it looks like some kind of version conflict between
// javapoet, guava and/or truth.
//noinspection GradleDependency
testImplementation ("com.google.testing.compile:compile-testing:0.10") {
// We don't use this and including it requires us to list it separatel which would be
// confusing.
exclude group: "com.google.auto.value", module: "auto-value"
}
testImplementation libs.androidx.annotation
testImplementation libs.androidx.fragment
// TODO: Find some way to include a similar dependency on java 9+ and re-enable these tests in gradle.
// testImplementation files(Jvm.current().getJre().homeDir.getAbsolutePath()+'/lib/rt.jar')
testAnnotationProcessor project(':annotation:compiler')
testAnnotationProcessor libs.autoservice
}
task regenerateTestResources {
group 'Verification'
description 'Regenerates all test resource files under annotation/compiler/test/src/test/resources that are compared against the current output to detect regressions'
tasks.withType(Test) {
systemProperties.put("com.bumptech.glide.annotation.compiler.test.regenerate.path", projectDir)
}
doFirst {
println("Regenerating test resources....")
}
doLast {
println("Finished regenerating test resources")
}
}
afterEvaluate {
regenerateTestResources.finalizedBy(testReleaseUnitTest)
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/AppGlideModuleWithExcludesTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.appResource;
import static com.bumptech.glide.annotation.compiler.test.Util.emptyLibraryModule;
import static com.bumptech.glide.annotation.compiler.test.Util.glide;
import static com.bumptech.glide.annotation.compiler.test.Util.subpackage;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.bumptech.glide.annotation.compiler.test.ReferencedResource;
import com.bumptech.glide.annotation.compiler.test.RegenerateResourcesRule;
import com.bumptech.glide.annotation.compiler.test.Util;
import com.google.testing.compile.Compilation;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests AppGlideModules that use the @Excludes annotation with a single excluded Module class. */
@RunWith(JUnit4.class)
public class AppGlideModuleWithExcludesTest implements CompilationProvider {
@Rule
public final RegenerateResourcesRule regenerateResourcesRule = new RegenerateResourcesRule(this);
private Compilation compilation;
@Before
public void setUp() {
compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(forResource("AppModuleWithExcludes.java"), emptyLibraryModule());
assertThat(compilation).succeededWithoutWarnings();
}
@Override
public Compilation getCompilation() {
return compilation;
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideOptionsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideOptions"))
.hasSourceEquivalentTo(appResource("GlideOptions.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideRequestClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequest"))
.hasSourceEquivalentTo(appResource("GlideRequest.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideRequestsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequests"))
.hasSourceEquivalentTo(appResource("GlideRequests.java"));
}
@Test
@ReferencedResource
public void compilationGeneratesExpectedGlideAppClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideApp"))
.hasSourceEquivalentTo(appResource("GlideApp.java"));
}
@Test
public void compilation_generatesExpectedGeneratedAppGlideModuleImpl() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedAppGlideModuleImpl"))
.hasSourceEquivalentTo(forResource("GeneratedAppGlideModuleImpl.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGeneratedRequestManagerFactory() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedRequestManagerFactory"))
.hasSourceEquivalentTo(appResource("GeneratedRequestManagerFactory.java"));
}
private JavaFileObject forResource(String name) {
return Util.forResource(getClass().getSimpleName(), name);
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/AppGlideModuleWithLibraryInPackageTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.appResource;
import static com.bumptech.glide.annotation.compiler.test.Util.glide;
import static com.bumptech.glide.annotation.compiler.test.Util.subpackage;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.bumptech.glide.annotation.compiler.test.ReferencedResource;
import com.bumptech.glide.annotation.compiler.test.RegenerateResourcesRule;
import com.bumptech.glide.annotation.compiler.test.Util;
import com.google.testing.compile.Compilation;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests AppGlideModules that use the @Excludes annotation with a single excluded Module class in a
* strangely named subpackage.
*/
@RunWith(JUnit4.class)
public class AppGlideModuleWithLibraryInPackageTest implements CompilationProvider {
@Rule
public final RegenerateResourcesRule regenerateResourcesRule = new RegenerateResourcesRule(this);
private Compilation compilation;
@Before
public void setUp() {
compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
forResource("AppModuleWithLibraryInPackage.java"),
forResource("LibraryModuleInPackage.java"));
assertThat(compilation).succeededWithoutWarnings();
}
@Override
public Compilation getCompilation() {
return compilation;
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideOptionsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideOptions"))
.hasSourceEquivalentTo(appResource("GlideOptions.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideRequestClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequest"))
.hasSourceEquivalentTo(appResource("GlideRequest.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideRequestsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequests"))
.hasSourceEquivalentTo(appResource("GlideRequests.java"));
}
@Test
@ReferencedResource
public void compilationGeneratesExpectedGlideAppClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideApp"))
.hasSourceEquivalentTo(appResource("GlideApp.java"));
}
@Test
public void compilation_generatesExpectedGeneratedAppGlideModuleImpl() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedAppGlideModuleImpl"))
.hasSourceEquivalentTo(forResource("GeneratedAppGlideModuleImpl.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGeneratedRequestManagerFactory() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedRequestManagerFactory"))
.hasSourceEquivalentTo(appResource("GeneratedRequestManagerFactory.java"));
}
private JavaFileObject forResource(String name) {
return Util.forResource(getClass().getSimpleName(), name);
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/AppGlideModuleWithMultipleExcludesTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.appResource;
import static com.bumptech.glide.annotation.compiler.test.Util.glide;
import static com.bumptech.glide.annotation.compiler.test.Util.subpackage;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.bumptech.glide.annotation.compiler.test.ReferencedResource;
import com.bumptech.glide.annotation.compiler.test.RegenerateResourcesRule;
import com.bumptech.glide.annotation.compiler.test.Util;
import com.google.testing.compile.Compilation;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests AppGlideModules that use the @Excludes annotation with multiple excluded Module classes.
*/
@RunWith(JUnit4.class)
public class AppGlideModuleWithMultipleExcludesTest implements CompilationProvider {
@Rule
public final RegenerateResourcesRule regenerateResourcesRule = new RegenerateResourcesRule(this);
private Compilation compilation;
@Before
public void setUp() {
compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
forResource("AppModuleWithMultipleExcludes.java"),
forResource("EmptyLibraryModule1.java"),
forResource("EmptyLibraryModule2.java"));
assertThat(compilation).succeededWithoutWarnings();
}
@Override
public Compilation getCompilation() {
return compilation;
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideOptionsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideOptions"))
.hasSourceEquivalentTo(appResource("GlideOptions.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideRequestClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequest"))
.hasSourceEquivalentTo(appResource("GlideRequest.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideRequestsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequests"))
.hasSourceEquivalentTo(appResource("GlideRequests.java"));
}
@Test
@ReferencedResource
public void compilationGeneratesExpectedGlideAppClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideApp"))
.hasSourceEquivalentTo(appResource("GlideApp.java"));
}
@Test
public void compilation_generatesExpectedGeneratedAppGlideModuleImpl() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedAppGlideModuleImpl"))
.hasSourceEquivalentTo(forResource("GeneratedAppGlideModuleImpl.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGeneratedRequestManagerFactory() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedRequestManagerFactory"))
.hasSourceEquivalentTo(appResource("GeneratedRequestManagerFactory.java"));
}
private JavaFileObject forResource(String name) {
return Util.forResource(getClass().getSimpleName(), name);
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/EmptyAppAndLibraryGlideModulesTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.annotation;
import static com.bumptech.glide.annotation.compiler.test.Util.appResource;
import static com.bumptech.glide.annotation.compiler.test.Util.emptyAppModule;
import static com.bumptech.glide.annotation.compiler.test.Util.emptyLibraryModule;
import static com.bumptech.glide.annotation.compiler.test.Util.glide;
import static com.bumptech.glide.annotation.compiler.test.Util.libraryResource;
import static com.bumptech.glide.annotation.compiler.test.Util.subpackage;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.bumptech.glide.annotation.compiler.test.ReferencedResource;
import com.bumptech.glide.annotation.compiler.test.RegenerateResourcesRule;
import com.bumptech.glide.annotation.compiler.test.Util;
import com.google.common.truth.Truth;
import com.google.testing.compile.Compilation;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests adding both an empty {@link com.bumptech.glide.module.AppGlideModule} and an empty {@link
* com.bumptech.glide.module.LibraryGlideModule} in a single project.
*/
@RunWith(JUnit4.class)
public class EmptyAppAndLibraryGlideModulesTest implements CompilationProvider {
@Rule
public final RegenerateResourcesRule regenerateResourcesRule = new RegenerateResourcesRule(this);
private Compilation compilation;
@Before
public void setUp() {
compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(emptyAppModule(), emptyLibraryModule());
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compilation_generatesAllExpectedFiles() {
Truth.assertThat(compilation.generatedSourceFiles()).hasSize(7);
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideOptionsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideOptions"))
.hasSourceEquivalentTo(appResource("GlideOptions.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideRequestClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequest"))
.hasSourceEquivalentTo(appResource("GlideRequest.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideRequestsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequests"))
.hasSourceEquivalentTo(appResource("GlideRequests.java"));
}
@Test
@ReferencedResource
public void compilationGeneratesExpectedGlideAppClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideApp"))
.hasSourceEquivalentTo(appResource("GlideApp.java"));
}
@Test
public void compilation_generatesExpectedGeneratedAppGlideModuleImpl() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedAppGlideModuleImpl"))
.hasSourceEquivalentTo(forResource("GeneratedAppGlideModuleImpl.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGeneratedRequestManagerFactory() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedRequestManagerFactory"))
.hasSourceEquivalentTo(appResource("GeneratedRequestManagerFactory.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedIndexer() throws IOException {
String expectedClassName =
"GlideIndexer_GlideModule_com_bumptech_glide_test_EmptyLibraryModule";
assertThat(compilation)
.generatedSourceFile(annotation(expectedClassName))
.hasSourceEquivalentTo(libraryResource(expectedClassName + ".java"));
}
private JavaFileObject forResource(String name) {
return Util.forResource(getClass().getSimpleName(), name);
}
@Override
public Compilation getCompilation() {
return compilation;
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/EmptyAppGlideModuleTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.glide;
import static com.bumptech.glide.annotation.compiler.test.Util.subpackage;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.bumptech.glide.annotation.compiler.test.RegenerateResourcesRule;
import com.bumptech.glide.annotation.compiler.test.Util;
import com.google.common.truth.Truth;
import com.google.testing.compile.Compilation;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests adding a single {@link com.bumptech.glide.test.EmptyAppModule} in a project. */
@RunWith(JUnit4.class)
public class EmptyAppGlideModuleTest implements CompilationProvider {
private static final String MODULE_NAME = "EmptyAppModule.java";
@Rule
public final RegenerateResourcesRule regenerateResourcesRule = new RegenerateResourcesRule(this);
private Compilation compilation;
@Before
public void setUp() {
compilation =
javac().withProcessors(new GlideAnnotationProcessor()).compile(forResource(MODULE_NAME));
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compilation_generatesAllExpectedFiles() {
Truth.assertThat(compilation.generatedSourceFiles()).hasSize(6);
}
@Test
public void compilation_generatesExpectedGlideOptionsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideOptions"))
.hasSourceEquivalentTo(forResource("GlideOptions.java"));
}
@Test
public void compilation_generatesExpectedGlideRequestClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequest"))
.hasSourceEquivalentTo(forResource("GlideRequest.java"));
}
@Test
public void compilation_generatesExpectedGlideRequestsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequests"))
.hasSourceEquivalentTo(forResource("GlideRequests.java"));
}
@Test
public void compilationGeneratesExpectedGlideAppClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideApp"))
.hasSourceEquivalentTo(forResource("GlideApp.java"));
}
@Test
public void compilation_generatesExpectedGeneratedAppGlideModuleImpl() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedAppGlideModuleImpl"))
.hasSourceEquivalentTo(forResource("GeneratedAppGlideModuleImpl.java"));
}
@Test
public void compilation_generatesExpectedGeneratedRequestManagerFactory() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedRequestManagerFactory"))
.hasSourceEquivalentTo(forResource("GeneratedRequestManagerFactory.java"));
}
private JavaFileObject forResource(String name) {
return Util.forResource(getClass().getSimpleName(), name);
}
@Override
public Compilation getCompilation() {
return compilation;
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/EmptyLibraryGlideModuleTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.annotation;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.bumptech.glide.annotation.compiler.test.RegenerateResourcesRule;
import com.bumptech.glide.annotation.compiler.test.Util;
import com.google.common.truth.Truth;
import com.google.testing.compile.Compilation;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests adding a single {@link com.bumptech.glide.module.LibraryGlideModule} in a project. */
@RunWith(JUnit4.class)
public class EmptyLibraryGlideModuleTest implements CompilationProvider {
@Rule
public final RegenerateResourcesRule regenerateResourcesRule = new RegenerateResourcesRule(this);
private static final String MODULE_NAME = "EmptyLibraryModule.java";
private Compilation compilation;
@Before
public void setUp() {
compilation =
javac().withProcessors(new GlideAnnotationProcessor()).compile(forResource(MODULE_NAME));
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compilation_generatesAllExpectedFiles() {
Truth.assertThat(compilation.generatedSourceFiles()).hasSize(1);
}
@Test
public void compilation_generatesExpectedIndexer() throws IOException {
String expectedClassName =
"GlideIndexer_GlideModule_com_bumptech_glide_test_EmptyLibraryModule";
assertThat(compilation)
.generatedSourceFile(annotation(expectedClassName))
.hasSourceEquivalentTo(forResource(expectedClassName + ".java"));
}
private JavaFileObject forResource(String name) {
return Util.forResource(getClass().getSimpleName(), name);
}
@Override
public Compilation getCompilation() {
return compilation;
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/GlideExtensionOptionsTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.emptyAppModule;
import static com.bumptech.glide.annotation.compiler.test.Util.subpackage;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.bumptech.glide.annotation.compiler.test.RegenerateResourcesRule;
import com.bumptech.glide.annotation.compiler.test.SubDirectory;
import com.bumptech.glide.annotation.compiler.test.TestDescription;
import com.bumptech.glide.annotation.compiler.test.Util;
import com.google.testing.compile.Compilation;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Verifies only the output we expect to change based on the various configurations of GlideOptions.
*/
@RunWith(JUnit4.class)
public class GlideExtensionOptionsTest implements CompilationProvider {
@Rule
public final RegenerateResourcesRule regenerateResourcesRule = new RegenerateResourcesRule(this);
@Rule public final TestDescription testDescription = new TestDescription();
private static final String EXTENSION_NAME = "Extension.java";
private Compilation currentCompilation;
@Test
@SubDirectory("OverrideExtend")
public void compilation_withOverrideExtend_validOptions() throws IOException {
runTest(Subject.GlideOptions);
}
@Test
@SubDirectory("OverrideExtend")
public void compilation_withOverrideExtend_validRequest() throws IOException {
runTest(Subject.GlideRequest);
}
@Test
@SubDirectory("OverrideExtendMultipleArguments")
public void compilation_withOverrideReplace_andMultipleArguments_validOptions()
throws IOException {
runTest(Subject.GlideOptions);
}
@Test
@SubDirectory("OverrideExtendMultipleArguments")
public void compilation_withOverrideReplace_andMultipleArguments_validRequest()
throws IOException {
runTest(Subject.GlideRequest);
}
@Test
@SubDirectory("OverrideReplace")
public void compilation_withOverrideReplace_validOptions() throws IOException {
runTest(Subject.GlideOptions);
}
@Test
@SubDirectory("OverrideReplace")
public void compilation_withOverrideReplace_validRequest() throws IOException {
runTest(Subject.GlideRequest);
}
@Test
@SubDirectory("StaticMethodName")
public void compilation_withStaticMethodName_validOptions() throws IOException {
runTest(Subject.GlideOptions);
}
@Test
@SubDirectory("StaticMethodName")
public void compilation_withStaticMethodName_validRequest() throws IOException {
runTest(Subject.GlideRequest);
}
@Test
@SubDirectory("MemoizeStaticMethod")
public void compilation_withMemoizeStaticMethod_validOptions() throws IOException {
runTest(Subject.GlideOptions);
}
@Test
@SubDirectory("MemoizeStaticMethod")
public void compilation_withMemoizeStaticMethod_validRequest() throws IOException {
runTest(Subject.GlideRequest);
}
@Test
@SubDirectory("SkipStaticMethod")
public void compilation_withSkipStaticMethod_validOptions() throws IOException {
runTest(Subject.GlideOptions);
}
@Test
@SubDirectory("SkipStaticMethod")
public void compilation_withSkipStaticMethod_validRequest() throws IOException {
runTest(Subject.GlideRequest);
}
@Override
public Compilation getCompilation() {
return currentCompilation;
}
private enum Subject {
GlideOptions,
GlideRequest;
String file() {
return name() + ".java";
}
}
private void runTest(Subject subject) {
String subDir = getSubDirectoryName();
currentCompilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(emptyAppModule(), extension(subDir));
assertThat(currentCompilation).succeededWithoutWarnings();
assertThat(currentCompilation)
.generatedSourceFile(subpackage(subject.name()))
.hasSourceEquivalentTo(forResource(subDir, subject.file()));
}
private String getSubDirectoryName() {
return testDescription.getDescription().getAnnotation(SubDirectory.class).value();
}
private JavaFileObject extension(String subdir) {
return forResource(subdir, EXTENSION_NAME);
}
private JavaFileObject forResource(String subdir, String name) {
return Util.forResource(getClass().getSimpleName(), subdir + "/" + name);
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/GlideExtensionWithOptionTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.appResource;
import static com.bumptech.glide.annotation.compiler.test.Util.emptyAppModule;
import static com.bumptech.glide.annotation.compiler.test.Util.glide;
import static com.bumptech.glide.annotation.compiler.test.Util.subpackage;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.bumptech.glide.annotation.compiler.test.ReferencedResource;
import com.bumptech.glide.annotation.compiler.test.RegenerateResourcesRule;
import com.bumptech.glide.annotation.compiler.test.Util;
import com.google.common.truth.Truth;
import com.google.testing.compile.Compilation;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Verifies the output of the processor with a simple single extension option in the new option
* style where extension methods always return values.
*/
@RunWith(JUnit4.class)
public class GlideExtensionWithOptionTest implements CompilationProvider {
@Rule
public final RegenerateResourcesRule regenerateResourcesRule = new RegenerateResourcesRule(this);
private Compilation compilation;
@Before
public void setUp() {
compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(emptyAppModule(), forResource("ExtensionWithOption.java"));
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compilation_generatesAllExpectedFiles() {
Truth.assertThat(compilation.generatedSourceFiles()).hasSize(7);
}
@Test
public void compilation_generatesExpectedGlideOptionsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideOptions"))
.hasSourceEquivalentTo(forResource("GlideOptions.java"));
}
@Test
public void compilation_generatesExpectedGlideRequestClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequest"))
.hasSourceEquivalentTo(forResource("GlideRequest.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideRequestsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequests"))
.hasSourceEquivalentTo(appResource("GlideRequests.java"));
}
@Test
@ReferencedResource
public void compilationGeneratesExpectedGlideAppClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideApp"))
.hasSourceEquivalentTo(appResource("GlideApp.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGeneratedAppGlideModuleImpl() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedAppGlideModuleImpl"))
.hasSourceEquivalentTo(appResource("GeneratedAppGlideModuleImpl.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGeneratedRequestManagerFactory() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedRequestManagerFactory"))
.hasSourceEquivalentTo(appResource("GeneratedRequestManagerFactory.java"));
}
private JavaFileObject forResource(String name) {
return Util.forResource(getClass().getSimpleName(), name);
}
@Override
public Compilation getCompilation() {
return compilation;
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/GlideExtensionWithTypeTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.appResource;
import static com.bumptech.glide.annotation.compiler.test.Util.emptyAppModule;
import static com.bumptech.glide.annotation.compiler.test.Util.glide;
import static com.bumptech.glide.annotation.compiler.test.Util.subpackage;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.bumptech.glide.annotation.compiler.test.ReferencedResource;
import com.bumptech.glide.annotation.compiler.test.RegenerateResourcesRule;
import com.bumptech.glide.annotation.compiler.test.Util;
import com.google.common.truth.Truth;
import com.google.testing.compile.Compilation;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Verifies the output of the processor with a simple single extension type. */
@RunWith(JUnit4.class)
public class GlideExtensionWithTypeTest implements CompilationProvider {
@Rule
public final RegenerateResourcesRule regenerateResourcesRule = new RegenerateResourcesRule(this);
private Compilation compilation;
@Before
public void setUp() {
compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(emptyAppModule(), forResource("ExtensionWithType.java"));
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compilation_generatesAllExpectedFiles() {
Truth.assertThat(compilation.generatedSourceFiles()).hasSize(7);
}
@Test
public void compilation_generatesExpectedGlideOptionsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideOptions"))
.hasSourceEquivalentTo(forResource("GlideOptions.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGlideRequestClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequest"))
.hasSourceEquivalentTo(appResource("GlideRequest.java"));
}
@Test
public void compilation_generatesExpectedGlideRequestsClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideRequests"))
.hasSourceEquivalentTo(forResource("GlideRequests.java"));
}
@Test
@ReferencedResource
public void compilationGeneratesExpectedGlideAppClass() throws IOException {
assertThat(compilation)
.generatedSourceFile(subpackage("GlideApp"))
.hasSourceEquivalentTo(appResource("GlideApp.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGeneratedAppGlideModuleImpl() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedAppGlideModuleImpl"))
.hasSourceEquivalentTo(appResource("GeneratedAppGlideModuleImpl.java"));
}
@Test
@ReferencedResource
public void compilation_generatesExpectedGeneratedRequestManagerFactory() throws IOException {
assertThat(compilation)
.generatedSourceFile(glide("GeneratedRequestManagerFactory"))
.hasSourceEquivalentTo(appResource("GeneratedRequestManagerFactory.java"));
}
private JavaFileObject forResource(String name) {
return Util.forResource(getClass().getSimpleName(), name);
}
@Override
public Compilation getCompilation() {
return compilation;
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/InvalidAppGlideModuleWithExcludesTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static org.junit.Assert.assertThrows;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests AppGlideModules with invalid usages of the @Excludes annotation. */
// Ignore warnings since most methods use assertThrows
@SuppressWarnings("ResultOfMethodCallIgnored")
@RunWith(JUnit4.class)
public class InvalidAppGlideModuleWithExcludesTest {
@Test
public void compilation_withMissingExcludedModuleClass_throws() {
assertThrows(
RuntimeException.class,
new ThrowingRunnable() {
@Override
public void run() throws Throwable {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
JavaFileObjects.forSourceLines(
"AppModuleWithExcludes",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.Excludes;",
"import com.bumptech.glide.annotation.GlideModule;",
"import com.bumptech.glide.module.AppGlideModule;",
"import com.bumptech.glide.test.EmptyLibraryModule;",
"@GlideModule",
"@Excludes(EmptyLibraryModule.class)",
"public final class AppModuleWithExcludes extends AppGlideModule {}"));
}
});
}
@Test
public void compilation_withEmptyExcludes_fails() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
JavaFileObjects.forSourceLines(
"AppModuleWithExcludes",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.Excludes;",
"import com.bumptech.glide.annotation.GlideModule;",
"import com.bumptech.glide.module.AppGlideModule;",
"import com.bumptech.glide.test.EmptyLibraryModule;",
"@GlideModule",
"@Excludes",
"public final class AppModuleWithExcludes extends AppGlideModule {}"));
assertThat(compilation).failed();
}
@Test
public void compilation_withNonGlideModule_throws() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
JavaFileObjects.forSourceLines(
"AppModuleWithExcludes",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.Excludes;",
"import com.bumptech.glide.annotation.GlideModule;",
"import com.bumptech.glide.module.AppGlideModule;",
"import com.bumptech.glide.test.EmptyLibraryModule;",
"@GlideModule",
"@Excludes(Object.class)",
"public final class AppModuleWithExcludes extends AppGlideModule {}"));
assertThat(compilation).failed();
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/InvalidGlideExtensionTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.emptyAppModule;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static org.junit.Assert.fail;
import com.google.common.truth.Truth;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Checks assertions on {@link com.bumptech.glide.annotation.GlideExtension}s themselves. */
// Avoid warnings when asserting on exceptions.
@SuppressWarnings("ResultOfMethodCallIgnored")
@RunWith(JUnit4.class)
public class InvalidGlideExtensionTest {
@Test
public void compilation_withPublicConstructor_fails() {
try {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"PublicConstructor",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"@GlideExtension",
"public class PublicConstructor { }"));
fail("Failed to throw expected exception");
} catch (RuntimeException e) {
Throwable cause = e.getCause();
Truth.assertThat(cause.getMessage()).contains("non-private constructor");
Truth.assertThat(cause.getMessage()).contains("PublicConstructor");
}
}
@Test
public void compilation_withPackagePrivateExtension_fails() {
try {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"PackagePrivateExtension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"@GlideExtension",
"class PackagePrivateExtension {",
" private PackagePrivateExtension() {}",
"}"));
fail("Failed to throw expected exception");
} catch (RuntimeException e) {
Throwable cause = e.getCause();
Truth.assertThat(cause.getMessage()).contains("must be public");
Truth.assertThat(cause.getMessage()).contains("PackagePrivateExtension");
}
}
@Test
public void compilation_withConstructorWithParameters_throws() {
try {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"ConstructorParametersExtension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"@GlideExtension",
"public class ConstructorParametersExtension {",
" private ConstructorParametersExtension(int failParam) {}",
" public void doSomething() {}",
"}"));
fail("Failed to get expected exception");
} catch (RuntimeException e) {
Throwable cause = e.getCause();
Truth.assertThat(cause.getMessage()).contains("parameters in the constructor");
Truth.assertThat(cause.getMessage()).contains("ConstructorParametersExtension");
}
}
@Test
public void compilation_withNonStaticMethod_succeeds() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" public void doSomething() {}",
"}"));
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compilation_withStaticMethod_succeeds() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" public static void doSomething() {}",
"}"));
assertThat(compilation).succeededWithoutWarnings();
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/InvalidGlideOptionsExtensionTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.emptyAppModule;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import com.google.common.truth.Truth;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Checks assertions on {@link com.bumptech.glide.annotation.GlideExtension}s for methods annotated
* with {@link com.bumptech.glide.annotation.GlideOption}.
*/
// Ignore warnings since most methods use assertThrows.
@SuppressWarnings("ResultOfMethodCallIgnored")
@RunWith(JUnit4.class)
public class InvalidGlideOptionsExtensionTest {
@Test
public void compilation_withAnnotatedNonStaticMethod_fails() {
assertThrows(
RuntimeException.class,
new ThrowingRunnable() {
@Override
public void run() {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @GlideOption",
" public void doSomething() {}",
"}"));
}
});
}
@Test
public void compilation_withAnnotatedStaticMethod_withRequestOptionsArgInWrongOrder_fails() {
try {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"NonRequestOptionsFirstArgExtension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class NonRequestOptionsFirstArgExtension{",
" private NonRequestOptionsFirstArgExtension() {}",
" @GlideOption",
" public static BaseRequestOptions> doSomething(",
" Object arg1, BaseRequestOptions> options) {",
" return options;",
" }",
"}"));
fail();
} catch (RuntimeException e) {
String message = e.getCause().getMessage();
Truth.assertThat(message).contains("BaseRequestOptions> object as their first parameter");
Truth.assertThat(message).contains("Object");
Truth.assertThat(message).contains("NonRequestOptionsFirstArgExtension");
}
}
@Test
public void compilation_withAnnotatedStaticMethod_withRequestOptionsArg_succeeds() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @GlideOption",
" public static BaseRequestOptions> doSomething(",
" BaseRequestOptions> options) {",
" return options;",
" }",
"}"));
assertThat(compilation).succeeded();
}
@Test
public void compilation_withAnnotatedStaticMethod_withRequestOptionsArgAndOtherArg_succeeds() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @GlideOption",
" public static BaseRequestOptions> doSomething(",
" BaseRequestOptions> options, Object arg2) {",
" return options;",
" }",
"}"));
assertThat(compilation).succeeded();
}
@Test
public void compilation_overridingOptionWithoutAnnotationType_fails() {
assertThrows(
RuntimeException.class,
new ThrowingRunnable() {
@Override
public void run() {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @GlideOption",
" public static BaseRequestOptions> centerCrop(",
" BaseRequestOptions> options) {",
" return options;",
" }",
"}"));
}
});
}
@Test
public void compilation_withOverrideExtend_butNotOverridingMethod_fails() {
assertThrows(
RuntimeException.class,
new ThrowingRunnable() {
@Override
public void run() {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @GlideOption(override = GlideOption.OVERRIDE_EXTEND)",
" public static BaseRequestOptions> something(",
" BaseRequestOptions> options) {",
" return options;",
" }",
"}"));
}
});
}
@Test
public void compilation_withOverrideExtend_andOverridingMethod_succeeds() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @GlideOption(override = GlideOption.OVERRIDE_EXTEND)",
" public static BaseRequestOptions> centerCrop(",
" BaseRequestOptions> options) {",
" return options;",
" }",
"}"));
assertThat(compilation).succeeded();
}
@Test
public void compilation_withOverrideReplace_butNotOverridingMethod_fails() {
assertThrows(
RuntimeException.class,
new ThrowingRunnable() {
@Override
public void run() {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @GlideOption(override = GlideOption.OVERRIDE_REPLACE)",
" public static BaseRequestOptions> something(",
" BaseRequestOptions> options) {",
" return options;",
" }",
"}"));
}
});
}
@Test
public void compilation_withOverrideReplace_andOverridingMethod_succeeds() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @GlideOption(override = GlideOption.OVERRIDE_REPLACE)",
" public static BaseRequestOptions> centerCrop(",
" BaseRequestOptions> options) {",
" return options;",
" }",
"}"));
assertThat(compilation).succeeded();
}
@Test
public void compilation_withRequestOptionsReturnValue_succeeds() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @NonNull",
" @GlideOption",
" public static BaseRequestOptions> doSomething(",
" BaseRequestOptions> options) {",
" return options;",
" }",
"}"));
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compilation_withNonRequestOptionsReturnValue_fails() {
try {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"WrongReturnTypeExtension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class WrongReturnTypeExtension {",
" private WrongReturnTypeExtension() {}",
" @NonNull",
" @GlideOption",
" public static Object doSomething(BaseRequestOptions> options) {",
" return options;",
" }",
"}"));
fail();
} catch (RuntimeException e) {
String message = e.getCause().getMessage();
Truth.assertThat(message)
.contains("@GlideOption methods should return a BaseRequestOptions> object");
Truth.assertThat(message).contains("Object");
Truth.assertThat(message).contains("WrongReturnTypeExtension");
}
}
@Test
public void compilation_withMissingNonNullAnnotation_warns() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @GlideOption",
" public static BaseRequestOptions> doSomething(",
" BaseRequestOptions> options) {",
" return options;",
" }",
"}"));
assertThat(compilation).succeeded();
assertThat(compilation).hadWarningCount(1);
assertThat(compilation).hadWarningContaining("androidx.annotation.NonNull");
assertThat(compilation).hadWarningContaining("com.bumptech.glide.test.Extension#doSomething");
}
@Test
public void compilation_withNoOptionParameters_fails() {
try {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"MissingRequestOptionsExtension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideOption;",
"import com.bumptech.glide.request.BaseRequestOptions;",
"@GlideExtension",
"public class MissingRequestOptionsExtension {",
" private MissingRequestOptionsExtension() {}",
" @NonNull",
" @GlideOption",
" public static BaseRequestOptions> doSomething() {",
" return options;",
" }",
"}"));
fail();
} catch (RuntimeException e) {
String message = e.getCause().getMessage();
Truth.assertThat(message).contains("BaseRequestOptions> object as their first parameter");
Truth.assertThat(message).contains("doSomething");
Truth.assertThat(message).contains("MissingRequestOptionsExtension");
}
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/InvalidGlideTypeExtensionTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.emptyAppModule;
import static com.bumptech.glide.annotation.compiler.test.Util.subpackage;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import com.google.common.truth.Truth;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Checks assertions on {@link com.bumptech.glide.annotation.GlideExtension}s for methods annotated
* with {@link com.bumptech.glide.annotation.GlideType}.
*/
// Ignore warnings since most methods use assertThrows.
@SuppressWarnings("ResultOfMethodCallIgnored")
@RunWith(JUnit4.class)
public class InvalidGlideTypeExtensionTest {
@Test
public void compilation_withAnnotatedNonStaticMethod_fails() {
assertThrows(
"@GlideType methods must be static",
RuntimeException.class,
new ThrowingRunnable() {
@Override
public void run() {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @NonNull",
" @GlideType(Number.class)",
" public RequestBuilder doSomething(",
" RequestBuilder builder) {",
" return builder;",
" }",
"}"));
}
});
}
@Test
public void compilation_withAnnotatedStaticMethod_withoutRequestBuilderArg_fails() {
assertThrows(
"@GlideType methods must take a RequestBuilder object as their first and only"
+ " parameter, but given multiple for:"
+ " com.bumptech.glide.test.Extension#doSomething()",
RuntimeException.class,
new ThrowingRunnable() {
@Override
public void run() {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @GlideType(Number.class)",
" public static RequestBuilder doSomething() {",
" return null;",
" }",
"}"));
}
});
}
@Test
public void compilation_withAnnotatedStaticMethod_withRequestBuilderArg_succeeds() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.RequestBuilder;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @NonNull",
" @GlideType(Number.class)",
" public static RequestBuilder type(RequestBuilder builder) {",
" return builder;",
" }",
"}"));
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compilation_withAnnotatedStaticMethod_withNonRequestBuilderArg_fails() {
try {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"WrongParameterTypeExtension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.RequestBuilder;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class WrongParameterTypeExtension {",
" private WrongParameterTypeExtension() {}",
" @NonNull",
" @GlideType(Number.class)",
" public static RequestBuilder type(Object arg) {",
" return null;",
" }",
"}"));
} catch (RuntimeException e) {
String message = e.getCause().getMessage();
Truth.assertThat(message).contains("RequestBuilder object as their first and only parameter");
Truth.assertThat(message).contains("Object");
Truth.assertThat(message).contains("WrongParameterTypeExtension");
}
}
@Test
public void compilation_withAnnotatedStaticMethod_withRequestBuilderArgAndOtherArg_fails() {
assertThrows(
"@GlideType methods must take a RequestBuilder object as their first and only"
+ " parameter, but given multiple for:"
+ " com.bumptech.glide.test.Extension#type("
+ "com.bumptech.glide.RequestBuilder,"
+ "java.lang.Object)",
RuntimeException.class,
new ThrowingRunnable() {
@Override
public void run() {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.RequestBuilder;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @NonNull",
" @GlideType(Number.class)",
" public static RequestBuilder type(",
" RequestBuilder builder, Object arg2) {",
" return builder;",
" }",
"}"));
}
});
}
@Test
public void compilation_withAnnotatedStaticMethod_overridingExistingType_fails() {
final Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import android.graphics.drawable.Drawable;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.RequestBuilder;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @NonNull",
" @GlideType(Drawable.class)",
" public static RequestBuilder asDrawable(",
" RequestBuilder builder) {",
" return builder;",
" }",
"}"));
assertThrows(
"error: method asDrawable() is already defined in class"
+ " com.bumptech.glide.test.GlideRequests",
RuntimeException.class,
new ThrowingRunnable() {
@Override
public void run() {
compilation.generatedSourceFile(subpackage("GlideRequests"));
}
});
}
@Test
public void compilation_withAnnotatedStaticMethod_returningRequestBuilder_succeeds() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.RequestBuilder;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @NonNull",
" @GlideType(Number.class)",
" public static RequestBuilder asNumber(",
" RequestBuilder builder) {",
" return builder;",
" }",
"}"));
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compilation_withAnnotatedStaticMethod_returningNonRequestBuilder_fails() {
try {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"WrongReturnTypeExtension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.RequestBuilder;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class WrongReturnTypeExtension {",
" private WrongReturnTypeExtension() {}",
" @NonNull",
" @GlideType(Number.class)",
" public static Object asNumber(",
" RequestBuilder builder) {",
" return new Object();",
" }",
"}"));
fail();
} catch (RuntimeException e) {
String message = e.getCause().getMessage();
Truth.assertThat(message).contains("@GlideType methods should return a RequestBuilder");
Truth.assertThat(message).contains("Number");
Truth.assertThat(message).contains("WrongReturnTypeExtension");
}
}
@Test
public void compilation_withAnnotatedStaticMethod_returningBuilderWithIncorrectType_fails() {
try {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"WrongBuilderTypeExtension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.RequestBuilder;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class WrongBuilderTypeExtension {",
" private WrongBuilderTypeExtension() {}",
" @NonNull",
" @GlideType(Number.class)",
" public static RequestBuilder asNumber(",
" RequestBuilder builder) {",
" return builder;",
" }",
"}"));
fail();
} catch (RuntimeException e) {
String message = e.getCause().getMessage();
Truth.assertThat(message)
.contains("@GlideType methods should return a RequestBuilder");
Truth.assertThat(message).contains("WrongBuilderTypeExtension");
}
}
@Test
public void compilation_withAnnotatedStaticMethod_returningBuilder_andMultipleParams_fails() {
assertThrows(
"@GlideType methods must take a RequestBuilder object as their first and only parameter,"
+ " but given multiple for:"
+ " com.bumptech.glide.test.Extension#asNumber("
+ "com.bumptech.glide.RequestBuilder,java.lang.Object)",
RuntimeException.class,
new ThrowingRunnable() {
@Override
public void run() {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.RequestBuilder;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @NonNull",
" @GlideType(Number.class)",
" public static RequestBuilder asNumber(",
" RequestBuilder builder, Object arg1) {",
" return builder;",
" }",
"}"));
}
});
}
@Test
public void compilation_withAnnotatedStaticMethod_returningBuilder_nonBuilderParam_fails() {
try {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"IncorrectParameterExtension",
"package com.bumptech.glide.test;",
"import androidx.annotation.NonNull;",
"import com.bumptech.glide.RequestBuilder;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class IncorrectParameterExtension {",
" private IncorrectParameterExtension() {}",
" @NonNull",
" @GlideType(Number.class)",
" public static RequestBuilder asNumber(",
" Object arg) {",
" return null;",
" }",
"}"));
fail();
} catch (RuntimeException e) {
String message = e.getCause().getMessage();
Truth.assertThat(message)
.contains(
"@GlideType methods must take a RequestBuilder object"
+ " as their first and only parameter");
Truth.assertThat(message).contains("Object");
Truth.assertThat(message).contains("IncorrectParameterExtension");
}
}
@Test
public void compilation_withAnnotatedStaticMethod_returningRequestBuilder_missingNonNull_warns() {
Compilation compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
emptyAppModule(),
JavaFileObjects.forSourceLines(
"Extension",
"package com.bumptech.glide.test;",
"import com.bumptech.glide.RequestBuilder;",
"import com.bumptech.glide.annotation.GlideExtension;",
"import com.bumptech.glide.annotation.GlideType;",
"@GlideExtension",
"public class Extension {",
" private Extension() {}",
" @GlideType(Number.class)",
" public static RequestBuilder asNumber(",
" RequestBuilder builder) {",
" return builder;",
" }",
"}"));
assertThat(compilation).succeeded();
assertThat(compilation).hadWarningCount(1);
assertThat(compilation).hadWarningContaining("androidx.annotation.NonNull");
assertThat(compilation).hadWarningContaining("com.bumptech.glide.test.Extension#asNumber");
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/MultipleAppGlideModuleTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static org.junit.Assert.assertThrows;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.bumptech.glide.annotation.compiler.test.RegenerateResourcesRule;
import com.bumptech.glide.annotation.compiler.test.Util;
import com.google.testing.compile.Compilation;
import javax.tools.JavaFileObject;
import org.junit.Rule;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Ensures that adding more than one {@link com.bumptech.glide.module.AppGlideModule} to a project
* will fail.
*/
@RunWith(JUnit4.class)
public class MultipleAppGlideModuleTest implements CompilationProvider {
private static final String FIRST_MODULE = "EmptyAppModule1.java";
private static final String SECOND_MODULE = "EmptyAppModule2.java";
@Rule
public final RegenerateResourcesRule regenerateResourcesRule = new RegenerateResourcesRule(this);
private Compilation compilation;
// Throws.
@SuppressWarnings("ResultOfMethodCallIgnored")
@Test
public void compilation_withTwoAppModules_fails() {
assertThrows(
RuntimeException.class,
new ThrowingRunnable() {
@Override
public void run() throws Throwable {
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(forResource(FIRST_MODULE), forResource(SECOND_MODULE));
}
});
}
@Test
public void compilation_withFirstModuleOnly_succeeds() {
compilation =
javac().withProcessors(new GlideAnnotationProcessor()).compile(forResource(FIRST_MODULE));
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compilation_withSecondModuleOnly_succeeds() {
compilation =
javac().withProcessors(new GlideAnnotationProcessor()).compile(forResource(SECOND_MODULE));
assertThat(compilation).succeededWithoutWarnings();
}
private JavaFileObject forResource(String name) {
return Util.forResource(getClass().getSimpleName(), name);
}
@Override
public Compilation getCompilation() {
return compilation;
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/MultipleEmptyLibraryGlideModuleTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.bumptech.glide.annotation.compiler.test.Util.annotation;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.bumptech.glide.annotation.compiler.test.RegenerateResourcesRule;
import com.bumptech.glide.annotation.compiler.test.Util;
import com.google.common.truth.Truth;
import com.google.testing.compile.Compilation;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests adding multiple {@link com.bumptech.glide.module.LibraryGlideModule}s in a project. */
@RunWith(JUnit4.class)
public class MultipleEmptyLibraryGlideModuleTest implements CompilationProvider {
@Rule
public final RegenerateResourcesRule regenerateResourcesRule = new RegenerateResourcesRule(this);
private Compilation compilation;
@Before
public void setUp() {
compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
forResource("EmptyLibraryModule1.java"), forResource("EmptyLibraryModule2.java"));
assertThat(compilation).succeededWithoutWarnings();
}
@Test
public void compilation_generatesAllExpectedFiles() {
Truth.assertThat(compilation.generatedSourceFiles()).hasSize(1);
}
@Test
public void compilation_generatesExpectedIndexerForModules() throws IOException {
String expectedClassName =
"GlideIndexer_GlideModule_com_bumptech_glide_test_EmptyLibraryModule1_com_bumptech_glide"
+ "_test_EmptyLibraryModule2";
assertThat(compilation)
.generatedSourceFile(annotation(expectedClassName))
.hasSourceEquivalentTo(forResource(expectedClassName + ".java"));
}
private JavaFileObject forResource(String name) {
return Util.forResource(getClass().getSimpleName(), name);
}
@Override
public Compilation getCompilation() {
return compilation;
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/OverlyLongFileNameTest.java
================================================
package com.bumptech.glide.annotation.compiler;
import static com.google.testing.compile.Compiler.javac;
import com.bumptech.glide.annotation.compiler.test.CompilationProvider;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.CompilationSubject;
import com.google.testing.compile.JavaFileObjects;
import java.io.File;
import java.io.IOException;
import javax.tools.JavaFileObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Makes sure that we can handle indexers based on long package or file names, or many modules.
*
* See #4106.
*/
@RunWith(JUnit4.class)
public class OverlyLongFileNameTest implements CompilationProvider {
@Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
private Compilation compilation;
private static final String FILE_NAME_LONGER_THAN_255_CHARS =
"SomeReallyReallyRidiculouslyLongFileNameOrPackageNameIGuessThatExceedsTwoHundredAndFiftyFive"
+ "CharactersThoughThatsOnlyAroundOneHundredCharactersWhichMeansINeedToKeepTypingToGetTo"
+ "TwoHundredAndFiftyFiveSomehowThankfullyOnlyLikeFiftyToGoNowMaybeButNotQuiteYet"
+ "SomewhereAroundNowIsProbablyGood";
@Before
public void setUp() {
compilation =
javac()
.withProcessors(new GlideAnnotationProcessor())
.compile(
JavaFileObjects.forSourceLines(
FILE_NAME_LONGER_THAN_255_CHARS,
"package com.bumptech.glide.test;",
"import com.bumptech.glide.annotation.GlideModule;",
"import com.bumptech.glide.module.LibraryGlideModule;",
"@GlideModule",
"public final class "
+ FILE_NAME_LONGER_THAN_255_CHARS
+ " extends LibraryGlideModule {}"));
}
@Test
public void compilingLongClassAndOrPackageNameShouldSucceed() throws IOException {
CompilationSubject.assertThat(compilation).succeededWithoutWarnings();
for (JavaFileObject file : compilation.generatedFiles()) {
temporaryFolder.create();
String actualFileName = new File(file.getName()).getName();
if (!actualFileName.startsWith(FILE_NAME_LONGER_THAN_255_CHARS)) {
try {
temporaryFolder.newFile(actualFileName).createNewFile();
} catch (IOException e) {
throw new RuntimeException("Failed to create: " + actualFileName, e);
}
}
}
}
@Override
public Compilation getCompilation() {
return compilation;
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/test/CompilationProvider.java
================================================
package com.bumptech.glide.annotation.compiler.test;
import com.google.testing.compile.Compilation;
/** Provides the {@link Compilation} used to compile test code. */
public interface CompilationProvider {
Compilation getCompilation();
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/test/ReferencedResource.java
================================================
package com.bumptech.glide.annotation.compiler.test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that the method in question is referencing a test resource that it doesn't "own" and
* should not attempt to regenerate.
*
*
Used by {@link RegenerateResourcesRule} to ensure that if we are regenerating resources, we're
* only regenerating them for a single class and only for the single class that has the correct name
* and directory sequence so that we update the correct file.
*
*
Ideally this wouldn't be necessary. It would be great if we could find a way to go from the
* test failure more directly to the actual path of the resource used. Right now we're basically
* guessing based on this annotation, the class name of the test class, and any values from {@link
* SubDirectory}. Without this annotation, we'd end up writing files that were never used.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReferencedResource {}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/test/RegenerateResourcesRule.java
================================================
package com.bumptech.glide.annotation.compiler.test;
import static com.bumptech.glide.annotation.compiler.test.Util.asUnixChars;
import androidx.annotation.NonNull;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import javax.tools.JavaFileObject;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
* Regenerates test resources for annotation compiler tests when the {@link
* Util#REGENERATE_TEST_RESOURCES_PROPERTY_NAME} property is set to the directory containing the
* project.
*
*
This can easily be used via gradle by running: {@code ./gradlew
* :annotation:compiler:test:regenerateTestResources }
*
*
Our regenerate task will set the appropriate environment variables that will allow the logic
* here to succeed. When running the tests normally, this class will do nothing.
*/
public final class RegenerateResourcesRule implements TestRule {
private CompilationProvider test;
public RegenerateResourcesRule(CompilationProvider test) {
this.test = test;
}
@Override
public Statement apply(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
try {
base.evaluate();
} catch (AssertionError e) {
String projectRoot = Util.getProjectRootIfRegeneratingTestResources();
if (projectRoot == null || description.getAnnotation(ReferencedResource.class) != null) {
throw e;
}
updateResourceFile(e, projectRoot, description);
}
}
};
}
private void updateResourceFile(
AssertionError e, @NonNull String projectDirectory, Description description) {
String testClassName = test.getClass().getSimpleName();
String testFileName = parseFileNameFromMessage(e);
String testDirectory = projectDirectory + "/src/test/resources/" + testClassName;
String subDirectorySegment =
description.getAnnotation(SubDirectory.class) != null
? description.getAnnotation(SubDirectory.class).value() + "/"
: "";
File expectedDirectory = new File(testDirectory + "/" + subDirectorySegment);
if (!expectedDirectory.exists() && !expectedDirectory.mkdirs()) {
throw new IllegalStateException(
"Failed to generate expected directory: " + expectedDirectory);
}
if (!expectedDirectory.isDirectory()) {
throw new IllegalStateException(
"Expected a directory, but found a file: " + expectedDirectory);
}
File expectedFile = new File(expectedDirectory, testFileName);
Writer writer = null;
try {
writer = new FileWriter(expectedFile);
writer.write(asUnixChars(parseActual(testFileName)).toString());
writer.close();
} catch (IOException e1) {
throw new RuntimeException("Failed to regenerate test file", e1);
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException exception) {
// Ignore.
}
}
}
}
private String parseActual(String fileName) {
for (JavaFileObject javaFileObject : test.getCompilation().generatedSourceFiles()) {
if (javaFileObject.getName().contains(fileName)) {
try {
return javaFileObject.getCharContent(true).toString();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
throw new IllegalStateException("Failed to find source file for name: " + fileName);
}
// Parses to GlideOptions.java.
private static String parseFileNameFromMessage(AssertionError e) {
String message = e.getMessage();
int firstGreaterThanIndex = message.indexOf('>');
String substring = message.substring(0, firstGreaterThanIndex);
int lastForwardSlashIndex = substring.lastIndexOf('/');
return substring.substring(lastForwardSlashIndex + 1, substring.length());
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/test/SubDirectory.java
================================================
package com.bumptech.glide.annotation.compiler.test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates the subdirectory for a particular test that contains the test resource(s) used for the
* method.
*
*
Used both by tests to extract the correct subdirectory and by the {@link
* RegenerateResourcesRule} for the same purpose.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SubDirectory {
String value();
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/test/TestDescription.java
================================================
package com.bumptech.glide.annotation.compiler.test;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
/**
* Exposes the {@link Description} for the current test, similar to {@link
* org.junit.rules.TestName}.
*/
public final class TestDescription extends TestWatcher {
private Description description;
@Override
protected void starting(Description description) {
this.description = description;
}
public Description getDescription() {
return description;
}
}
================================================
FILE: annotation/compiler/test/src/test/java/com/bumptech/glide/annotation/compiler/test/Util.java
================================================
package com.bumptech.glide.annotation.compiler.test;
import com.google.testing.compile.JavaFileObjects;
import javax.tools.JavaFileObject;
/** Test utilities. */
public final class Util {
private static final String REGENERATE_TEST_RESOURCES_PROPERTY_NAME =
"com.bumptech.glide.annotation.compiler.test.regenerate.path";
private static final String GLIDE_PACKAGE_NAME = "com.bumptech.glide";
private static final String SUB_PACKAGE_NAME = qualified(GLIDE_PACKAGE_NAME, "test");
private static final String ANNOTATION_PACKAGE_NAME = "com.bumptech.glide.annotation.compiler";
private static final String DEFAULT_APP_DIR_NAME = "EmptyAppGlideModuleTest";
private static final String DEFAULT_LIBRARY_DIR_NAME = "EmptyLibraryGlideModuleTest";
/**
* Hardcoded file separator to workaround {@code JavaFileObjects.forResource(...)} defaulting to
* the unix one.
*/
private static final String FILE_SEPARATOR = "/";
private static final String LINE_SEPARATOR = "\n";
private Util() {
// Utility class.
}
/**
* Returns the {@code String} from a system property that is expected to contain the project
* directory for the module containing these tests or {@code null} if we're not currently
* attempting to regenerate test resources.
*/
static String getProjectRootIfRegeneratingTestResources() {
return System.getProperty(REGENERATE_TEST_RESOURCES_PROPERTY_NAME);
}
public static JavaFileObject emptyAppModule() {
return appResource("EmptyAppModule.java");
}
public static JavaFileObject emptyLibraryModule() {
return libraryResource("EmptyLibraryModule.java");
}
public static JavaFileObject appResource(String className) {
return forResource(DEFAULT_APP_DIR_NAME, className);
}
public static JavaFileObject libraryResource(String className) {
return forResource(DEFAULT_LIBRARY_DIR_NAME, className);
}
public static JavaFileObject forResource(String directoryName, String name) {
try {
return JavaFileObjects.forResource(directoryName + FILE_SEPARATOR + name);
} catch (IllegalArgumentException e) {
// IllegalArgumentException will be thrown if the resource is missing. If we're trying to
// generate test resources for a new test, we want to avoid this exception because it does not
// contain any expected output that we can write to a file. By returning an empty file, we
// avoid the exception and get the output from our comparison tests that we can then write
// out.
// If we're not regenerating test resources, we should throw the normal exception.
if (getProjectRootIfRegeneratingTestResources() != null) {
return JavaFileObjects.forSourceString("com.bumptech.test.empty", "");
}
throw e;
}
}
public static String annotation(String className) {
return qualified(ANNOTATION_PACKAGE_NAME, className);
}
public static String subpackage(String className) {
return qualified(SUB_PACKAGE_NAME, className);
}
public static String glide(String className) {
return qualified(GLIDE_PACKAGE_NAME, className);
}
public static CharSequence asUnixChars(CharSequence chars) {
return chars.toString().replace(System.lineSeparator(), LINE_SEPARATOR);
}
private static String qualified(String packageName, String className) {
return packageName + '.' + className;
}
}
================================================
FILE: annotation/compiler/test/src/test/resources/AppGlideModuleWithExcludesTest/AppModuleWithExcludes.java
================================================
package com.bumptech.glide.test;
import com.bumptech.glide.annotation.Excludes;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
@GlideModule
@Excludes(EmptyLibraryModule.class)
public final class AppModuleWithExcludes extends AppGlideModule {}
================================================
FILE: annotation/compiler/test/src/test/resources/AppGlideModuleWithExcludesTest/GeneratedAppGlideModuleImpl.java
================================================
package com.bumptech.glide;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import com.bumptech.glide.test.AppModuleWithExcludes;
import java.util.HashSet;
import java.util.Set;
@SuppressWarnings("deprecation")
final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
private final AppModuleWithExcludes appGlideModule;
public GeneratedAppGlideModuleImpl(Context context) {
appGlideModule = new AppModuleWithExcludes();
if (Log.isLoggable("Glide", Log.DEBUG)) {
Log.d("Glide", "Discovered AppGlideModule from annotation: com.bumptech.glide.test.AppModuleWithExcludes");
Log.d("Glide", "AppGlideModule excludes LibraryGlideModule from annotation: com.bumptech.glide.test.EmptyLibraryModule");
}
}
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
appGlideModule.applyOptions(context, builder);
}
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide,
@NonNull Registry registry) {
appGlideModule.registerComponents(context, glide, registry);
}
@Override
public boolean isManifestParsingEnabled() {
return appGlideModule.isManifestParsingEnabled();
}
@Override
@NonNull
public Set> getExcludedModuleClasses() {
Set> excludedClasses = new HashSet>();
excludedClasses.add(com.bumptech.glide.test.EmptyLibraryModule.class);
return excludedClasses;
}
@Override
@NonNull
GeneratedRequestManagerFactory getRequestManagerFactory() {
return new GeneratedRequestManagerFactory();
}
}
================================================
FILE: annotation/compiler/test/src/test/resources/AppGlideModuleWithLibraryInPackageTest/AppModuleWithLibraryInPackage.java
================================================
package com.bumptech.glide.test;
import com.bumptech.glide.annotation.Excludes;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
import com.bumptech.glide.test._package.LibraryModuleInPackage;
@GlideModule
@Excludes(LibraryModuleInPackage.class)
public final class AppModuleWithLibraryInPackage extends AppGlideModule {}
================================================
FILE: annotation/compiler/test/src/test/resources/AppGlideModuleWithLibraryInPackageTest/GeneratedAppGlideModuleImpl.java
================================================
package com.bumptech.glide;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import com.bumptech.glide.test.AppModuleWithLibraryInPackage;
import java.util.HashSet;
import java.util.Set;
@SuppressWarnings("deprecation")
final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
private final AppModuleWithLibraryInPackage appGlideModule;
public GeneratedAppGlideModuleImpl(Context context) {
appGlideModule = new AppModuleWithLibraryInPackage();
if (Log.isLoggable("Glide", Log.DEBUG)) {
Log.d("Glide", "Discovered AppGlideModule from annotation: com.bumptech.glide.test.AppModuleWithLibraryInPackage");
Log.d("Glide", "AppGlideModule excludes LibraryGlideModule from annotation: com.bumptech.glide.test._package.LibraryModuleInPackage");
}
}
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
appGlideModule.applyOptions(context, builder);
}
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide,
@NonNull Registry registry) {
appGlideModule.registerComponents(context, glide, registry);
}
@Override
public boolean isManifestParsingEnabled() {
return appGlideModule.isManifestParsingEnabled();
}
@Override
@NonNull
public Set> getExcludedModuleClasses() {
Set> excludedClasses = new HashSet>();
excludedClasses.add(com.bumptech.glide.test._package.LibraryModuleInPackage.class);
return excludedClasses;
}
@Override
@NonNull
GeneratedRequestManagerFactory getRequestManagerFactory() {
return new GeneratedRequestManagerFactory();
}
}
================================================
FILE: annotation/compiler/test/src/test/resources/AppGlideModuleWithLibraryInPackageTest/LibraryModuleInPackage.java
================================================
// _ in the name is important otherwise everything would work
package com.bumptech.glide.test._package;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.LibraryGlideModule;
@GlideModule
public final class LibraryModuleInPackage extends LibraryGlideModule {}
================================================
FILE: annotation/compiler/test/src/test/resources/AppGlideModuleWithMultipleExcludesTest/AppModuleWithMultipleExcludes.java
================================================
package com.bumptech.glide.test;
import com.bumptech.glide.annotation.Excludes;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
@GlideModule
@Excludes({EmptyLibraryModule1.class, EmptyLibraryModule2.class})
public final class AppModuleWithMultipleExcludes extends AppGlideModule {}
================================================
FILE: annotation/compiler/test/src/test/resources/AppGlideModuleWithMultipleExcludesTest/EmptyLibraryModule1.java
================================================
package com.bumptech.glide.test;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.LibraryGlideModule;
@GlideModule
public final class EmptyLibraryModule1 extends LibraryGlideModule {}
================================================
FILE: annotation/compiler/test/src/test/resources/AppGlideModuleWithMultipleExcludesTest/EmptyLibraryModule2.java
================================================
package com.bumptech.glide.test;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.LibraryGlideModule;
@GlideModule
public final class EmptyLibraryModule2 extends LibraryGlideModule {}
================================================
FILE: annotation/compiler/test/src/test/resources/AppGlideModuleWithMultipleExcludesTest/GeneratedAppGlideModuleImpl.java
================================================
package com.bumptech.glide;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import com.bumptech.glide.test.AppModuleWithMultipleExcludes;
import java.util.HashSet;
import java.util.Set;
@SuppressWarnings("deprecation")
final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
private final AppModuleWithMultipleExcludes appGlideModule;
public GeneratedAppGlideModuleImpl(Context context) {
appGlideModule = new AppModuleWithMultipleExcludes();
if (Log.isLoggable("Glide", Log.DEBUG)) {
Log.d("Glide", "Discovered AppGlideModule from annotation: com.bumptech.glide.test.AppModuleWithMultipleExcludes");
Log.d("Glide", "AppGlideModule excludes LibraryGlideModule from annotation: com.bumptech.glide.test.EmptyLibraryModule1");
Log.d("Glide", "AppGlideModule excludes LibraryGlideModule from annotation: com.bumptech.glide.test.EmptyLibraryModule2");
}
}
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
appGlideModule.applyOptions(context, builder);
}
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide,
@NonNull Registry registry) {
appGlideModule.registerComponents(context, glide, registry);
}
@Override
public boolean isManifestParsingEnabled() {
return appGlideModule.isManifestParsingEnabled();
}
@Override
@NonNull
public Set> getExcludedModuleClasses() {
Set> excludedClasses = new HashSet>();
excludedClasses.add(com.bumptech.glide.test.EmptyLibraryModule1.class);
excludedClasses.add(com.bumptech.glide.test.EmptyLibraryModule2.class);
return excludedClasses;
}
@Override
@NonNull
GeneratedRequestManagerFactory getRequestManagerFactory() {
return new GeneratedRequestManagerFactory();
}
}
================================================
FILE: annotation/compiler/test/src/test/resources/EmptyAppAndLibraryGlideModulesTest/GeneratedAppGlideModuleImpl.java
================================================
package com.bumptech.glide;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import com.bumptech.glide.test.EmptyAppModule;
import com.bumptech.glide.test.EmptyLibraryModule;
import java.util.Collections;
import java.util.Set;
@SuppressWarnings("deprecation")
final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
private final EmptyAppModule appGlideModule;
public GeneratedAppGlideModuleImpl(Context context) {
appGlideModule = new EmptyAppModule();
if (Log.isLoggable("Glide", Log.DEBUG)) {
Log.d("Glide", "Discovered AppGlideModule from annotation: com.bumptech.glide.test.EmptyAppModule");
Log.d("Glide", "Discovered LibraryGlideModule from annotation: com.bumptech.glide.test.EmptyLibraryModule");
}
}
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
appGlideModule.applyOptions(context, builder);
}
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide,
@NonNull Registry registry) {
new EmptyLibraryModule().registerComponents(context, glide, registry);
appGlideModule.registerComponents(context, glide, registry);
}
@Override
public boolean isManifestParsingEnabled() {
return appGlideModule.isManifestParsingEnabled();
}
@Override
@NonNull
public Set> getExcludedModuleClasses() {
return Collections.emptySet();
}
@Override
@NonNull
GeneratedRequestManagerFactory getRequestManagerFactory() {
return new GeneratedRequestManagerFactory();
}
}
================================================
FILE: annotation/compiler/test/src/test/resources/EmptyAppGlideModuleTest/EmptyAppModule.java
================================================
package com.bumptech.glide.test;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
@GlideModule
public final class EmptyAppModule extends AppGlideModule {}
================================================
FILE: annotation/compiler/test/src/test/resources/EmptyAppGlideModuleTest/GeneratedAppGlideModuleImpl.java
================================================
package com.bumptech.glide;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import com.bumptech.glide.test.EmptyAppModule;
import java.util.Collections;
import java.util.Set;
@SuppressWarnings("deprecation")
final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
private final EmptyAppModule appGlideModule;
public GeneratedAppGlideModuleImpl(Context context) {
appGlideModule = new EmptyAppModule();
if (Log.isLoggable("Glide", Log.DEBUG)) {
Log.d("Glide", "Discovered AppGlideModule from annotation: com.bumptech.glide.test.EmptyAppModule");
}
}
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
appGlideModule.applyOptions(context, builder);
}
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide,
@NonNull Registry registry) {
appGlideModule.registerComponents(context, glide, registry);
}
@Override
public boolean isManifestParsingEnabled() {
return appGlideModule.isManifestParsingEnabled();
}
@Override
@NonNull
public Set> getExcludedModuleClasses() {
return Collections.emptySet();
}
@Override
@NonNull
GeneratedRequestManagerFactory getRequestManagerFactory() {
return new GeneratedRequestManagerFactory();
}
}
================================================
FILE: annotation/compiler/test/src/test/resources/EmptyAppGlideModuleTest/GeneratedRequestManagerFactory.java
================================================
package com.bumptech.glide;
import android.content.Context;
import androidx.annotation.NonNull;
import com.bumptech.glide.manager.Lifecycle;
import com.bumptech.glide.manager.RequestManagerRetriever;
import com.bumptech.glide.manager.RequestManagerTreeNode;
import com.bumptech.glide.test.GlideRequests;
/**
* Generated code, do not modify
*/
final class GeneratedRequestManagerFactory implements RequestManagerRetriever.RequestManagerFactory {
@Override
@NonNull
public RequestManager build(@NonNull Glide glide, @NonNull Lifecycle lifecycle,
@NonNull RequestManagerTreeNode treeNode, @NonNull Context context) {
return new GlideRequests(glide, lifecycle, treeNode, context);
}
}
================================================
FILE: annotation/compiler/test/src/test/resources/EmptyAppGlideModuleTest/GlideApp.java
================================================
package com.bumptech.glide.test;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import java.io.File;
/**
* The entry point for interacting with Glide for Applications
*
* Includes all generated APIs from all
* {@link com.bumptech.glide.annotation.GlideExtension}s in source and dependent libraries.
*
*
This class is generated and should not be modified
* @see Glide
*/
public final class GlideApp {
private GlideApp() {
}
/**
* @see Glide#getPhotoCacheDir(Context)
*/
@Nullable
public static File getPhotoCacheDir(@NonNull Context context) {
return Glide.getPhotoCacheDir(context);
}
/**
* @see Glide#getPhotoCacheDir(Context, String)
*/
@Nullable
public static File getPhotoCacheDir(@NonNull Context context, @NonNull String string) {
return Glide.getPhotoCacheDir(context, string);
}
/**
* @see Glide#get(Context)
*/
@NonNull
public static Glide get(@NonNull Context context) {
return Glide.get(context);
}
/**
* @see Glide#init(Glide)
*/
@Deprecated
@VisibleForTesting
@SuppressLint("VisibleForTests")
public static void init(Glide glide) {
Glide.init(glide);
}
/**
* @see Glide#init(Context, GlideBuilder)
*/
@VisibleForTesting
@SuppressLint("VisibleForTests")
public static void init(@NonNull Context context, @NonNull GlideBuilder builder) {
Glide.init(context, builder);
}
/**
* @see Glide#enableHardwareBitmaps()
*/
@VisibleForTesting
@SuppressLint("VisibleForTests")
public static void enableHardwareBitmaps() {
Glide.enableHardwareBitmaps();
}
/**
* @see Glide#tearDown()
*/
@VisibleForTesting
@SuppressLint("VisibleForTests")
public static void tearDown() {
Glide.tearDown();
}
/**
* @see Glide#with(Context)
*/
@NonNull
public static GlideRequests with(@NonNull Context context) {
return (GlideRequests) Glide.with(context);
}
/**
* @see Glide#with(Activity)
*/
@Deprecated
@NonNull
public static GlideRequests with(@NonNull Activity activity) {
return (GlideRequests) Glide.with(activity);
}
/**
* @see Glide#with(FragmentActivity)
*/
@NonNull
public static GlideRequests with(@NonNull FragmentActivity activity) {
return (GlideRequests) Glide.with(activity);
}
/**
* @see Glide#with(Fragment)
*/
@NonNull
public static GlideRequests with(@NonNull Fragment fragment) {
return (GlideRequests) Glide.with(fragment);
}
/**
* @see Glide#with(Fragment)
*/
@Deprecated
@NonNull
public static GlideRequests with(@NonNull android.app.Fragment fragment) {
return (GlideRequests) Glide.with(fragment);
}
/**
* @see Glide#with(View)
*/
@NonNull
public static GlideRequests with(@NonNull View view) {
return (GlideRequests) Glide.with(view);
}
}
================================================
FILE: annotation/compiler/test/src/test/resources/EmptyAppGlideModuleTest/GlideOptions.java
================================================
package com.bumptech.glide.test;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import androidx.annotation.CheckResult;
import androidx.annotation.DrawableRes;
import androidx.annotation.FloatRange;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DecodeFormat;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.Option;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
import com.bumptech.glide.request.BaseRequestOptions;
import com.bumptech.glide.request.RequestOptions;
/**
* Automatically generated from {@link com.bumptech.glide.annotation.GlideExtension} annotated classes.
*
* @see RequestOptions
*/
@SuppressWarnings("deprecation")
public final class GlideOptions extends RequestOptions implements Cloneable {
private static GlideOptions fitCenterTransform0;
private static GlideOptions centerInsideTransform1;
private static GlideOptions centerCropTransform2;
private static GlideOptions circleCropTransform3;
private static GlideOptions noTransformation4;
private static GlideOptions noAnimation5;
/**
* @see RequestOptions#sizeMultiplierOf(float)
*/
@CheckResult
@NonNull
public static GlideOptions sizeMultiplierOf(@FloatRange(from = 0.0, to = 1.0) float value) {
return new GlideOptions().sizeMultiplier(value);
}
/**
* @see RequestOptions#diskCacheStrategyOf(DiskCacheStrategy)
*/
@CheckResult
@NonNull
public static GlideOptions diskCacheStrategyOf(@NonNull DiskCacheStrategy strategy) {
return new GlideOptions().diskCacheStrategy(strategy);
}
/**
* @see RequestOptions#priorityOf(Priority)
*/
@CheckResult
@NonNull
public static GlideOptions priorityOf(@NonNull Priority priority) {
return new GlideOptions().priority(priority);
}
/**
* @see RequestOptions#placeholderOf(Drawable)
*/
@CheckResult
@NonNull
public static GlideOptions placeholderOf(@Nullable Drawable drawable) {
return new GlideOptions().placeholder(drawable);
}
/**
* @see RequestOptions#placeholderOf(int)
*/
@CheckResult
@NonNull
public static GlideOptions placeholderOf(@DrawableRes int id) {
return new GlideOptions().placeholder(id);
}
/**
* @see RequestOptions#errorOf(Drawable)
*/
@CheckResult
@NonNull
public static GlideOptions errorOf(@Nullable Drawable drawable) {
return new GlideOptions().error(drawable);
}
/**
* @see RequestOptions#errorOf(int)
*/
@CheckResult
@NonNull
public static GlideOptions errorOf(@DrawableRes int id) {
return new GlideOptions().error(id);
}
/**
* @see RequestOptions#skipMemoryCacheOf(boolean)
*/
@CheckResult
@NonNull
public static GlideOptions skipMemoryCacheOf(boolean skipMemoryCache) {
return new GlideOptions().skipMemoryCache(skipMemoryCache);
}
/**
* @see RequestOptions#overrideOf(int, int)
*/
@CheckResult
@NonNull
public static GlideOptions overrideOf(int width, int height) {
return new GlideOptions().override(width, height);
}
/**
* @see RequestOptions#overrideOf(int)
*/
@CheckResult
@NonNull
public static GlideOptions overrideOf(int size) {
return new GlideOptions().override(size);
}
/**
* @see RequestOptions#signatureOf(Key)
*/
@CheckResult
@NonNull
public static GlideOptions signatureOf(@NonNull Key key) {
return new GlideOptions().signature(key);
}
/**
* @see RequestOptions#fitCenterTransform()
*/
@CheckResult
@NonNull
public static GlideOptions fitCenterTransform() {
if (GlideOptions.fitCenterTransform0 == null) {
GlideOptions.fitCenterTransform0 =
new GlideOptions().fitCenter().autoClone();
}
return GlideOptions.fitCenterTransform0;
}
/**
* @see RequestOptions#centerInsideTransform()
*/
@CheckResult
@NonNull
public static GlideOptions centerInsideTransform() {
if (GlideOptions.centerInsideTransform1 == null) {
GlideOptions.centerInsideTransform1 =
new GlideOptions().centerInside().autoClone();
}
return GlideOptions.centerInsideTransform1;
}
/**
* @see RequestOptions#centerCropTransform()
*/
@CheckResult
@NonNull
public static GlideOptions centerCropTransform() {
if (GlideOptions.centerCropTransform2 == null) {
GlideOptions.centerCropTransform2 =
new GlideOptions().centerCrop().autoClone();
}
return GlideOptions.centerCropTransform2;
}
/**
* @see RequestOptions#circleCropTransform()
*/
@CheckResult
@NonNull
public static GlideOptions circleCropTransform() {
if (GlideOptions.circleCropTransform3 == null) {
GlideOptions.circleCropTransform3 =
new GlideOptions().circleCrop().autoClone();
}
return GlideOptions.circleCropTransform3;
}
/**
* @see RequestOptions#bitmapTransform(Transformation)
*/
@CheckResult
@NonNull
public static GlideOptions bitmapTransform(@NonNull Transformation transformation) {
return new GlideOptions().transform(transformation);
}
/**
* @see RequestOptions#noTransformation()
*/
@CheckResult
@NonNull
public static GlideOptions noTransformation() {
if (GlideOptions.noTransformation4 == null) {
GlideOptions.noTransformation4 =
new GlideOptions().dontTransform().autoClone();
}
return GlideOptions.noTransformation4;
}
/**
* @see RequestOptions#option(Option, T)
*/
@CheckResult
@NonNull
public static GlideOptions option(@NonNull Option option, @NonNull T t) {
return new GlideOptions().set(option, t);
}
/**
* @see RequestOptions#decodeTypeOf(Class)
*/
@CheckResult
@NonNull
public static GlideOptions decodeTypeOf(@NonNull Class> clazz) {
return new GlideOptions().decode(clazz);
}
/**
* @see RequestOptions#formatOf(DecodeFormat)
*/
@CheckResult
@NonNull
public static GlideOptions formatOf(@NonNull DecodeFormat format) {
return new GlideOptions().format(format);
}
/**
* @see RequestOptions#frameOf(long)
*/
@CheckResult
@NonNull
public static GlideOptions frameOf(@IntRange(from = 0) long value) {
return new GlideOptions().frame(value);
}
/**
* @see RequestOptions#downsampleOf(DownsampleStrategy)
*/
@CheckResult
@NonNull
public static GlideOptions downsampleOf(@NonNull DownsampleStrategy strategy) {
return new GlideOptions().downsample(strategy);
}
/**
* @see RequestOptions#timeoutOf(int)
*/
@CheckResult
@NonNull
public static GlideOptions timeoutOf(@IntRange(from = 0) int value) {
return new GlideOptions().timeout(value);
}
/**
* @see RequestOptions#encodeQualityOf(int)
*/
@CheckResult
@NonNull
public static GlideOptions encodeQualityOf(@IntRange(from = 0, to = 100) int value) {
return new GlideOptions().encodeQuality(value);
}
/**
* @see RequestOptions#encodeFormatOf(CompressFormat)
*/
@CheckResult
@NonNull
public static GlideOptions encodeFormatOf(@NonNull Bitmap.CompressFormat format) {
return new GlideOptions().encodeFormat(format);
}
/**
* @see RequestOptions#noAnimation()
*/
@CheckResult
@NonNull
public static GlideOptions noAnimation() {
if (GlideOptions.noAnimation5 == null) {
GlideOptions.noAnimation5 =
new GlideOptions().dontAnimate().autoClone();
}
return GlideOptions.noAnimation5;
}
@Override
@NonNull
@CheckResult
public GlideOptions sizeMultiplier(@FloatRange(from = 0.0, to = 1.0) float value) {
return (GlideOptions) super.sizeMultiplier(value);
}
@Override
@NonNull
@CheckResult
public GlideOptions useUnlimitedSourceGeneratorsPool(boolean flag) {
return (GlideOptions) super.useUnlimitedSourceGeneratorsPool(flag);
}
@Override
@NonNull
@CheckResult
public GlideOptions useAnimationPool(boolean flag) {
return (GlideOptions) super.useAnimationPool(flag);
}
@Override
@NonNull
@CheckResult
public GlideOptions onlyRetrieveFromCache(boolean flag) {
return (GlideOptions) super.onlyRetrieveFromCache(flag);
}
@Override
@NonNull
@CheckResult
public GlideOptions diskCacheStrategy(@NonNull DiskCacheStrategy strategy) {
return (GlideOptions) super.diskCacheStrategy(strategy);
}
@Override
@NonNull
@CheckResult
public GlideOptions priority(@NonNull Priority priority) {
return (GlideOptions) super.priority(priority);
}
@Override
@NonNull
@CheckResult
public GlideOptions placeholder(@Nullable Drawable drawable) {
return (GlideOptions) super.placeholder(drawable);
}
@Override
@NonNull
@CheckResult
public GlideOptions placeholder(@DrawableRes int id) {
return (GlideOptions) super.placeholder(id);
}
@Override
@NonNull
@CheckResult
public GlideOptions fallback(@Nullable Drawable drawable) {
return (GlideOptions) super.fallback(drawable);
}
@Override
@NonNull
@CheckResult
public GlideOptions fallback(@DrawableRes int id) {
return (GlideOptions) super.fallback(id);
}
@Override
@NonNull
@CheckResult
public GlideOptions error(@Nullable Drawable drawable) {
return (GlideOptions) super.error(drawable);
}
@Override
@NonNull
@CheckResult
public GlideOptions error(@DrawableRes int id) {
return (GlideOptions) super.error(id);
}
@Override
@NonNull
@CheckResult
public GlideOptions theme(@Nullable Resources.Theme theme) {
return (GlideOptions) super.theme(theme);
}
@Override
@NonNull
@CheckResult
public GlideOptions skipMemoryCache(boolean skip) {
return (GlideOptions) super.skipMemoryCache(skip);
}
@Override
@NonNull
@CheckResult
public GlideOptions override(int width, int height) {
return (GlideOptions) super.override(width, height);
}
@Override
@NonNull
@CheckResult
public GlideOptions override(int size) {
return (GlideOptions) super.override(size);
}
@Override
@NonNull
@CheckResult
public GlideOptions signature(@NonNull Key key) {
return (GlideOptions) super.signature(key);
}
@Override
@CheckResult
public GlideOptions clone() {
return (GlideOptions) super.clone();
}
@Override
@NonNull
@CheckResult
public GlideOptions set(@NonNull Option option, @NonNull Y y) {
return (GlideOptions) super.set(option, y);
}
@Override
@NonNull
@CheckResult
public GlideOptions decode(@NonNull Class> clazz) {
return (GlideOptions) super.decode(clazz);
}
@Override
@NonNull
@CheckResult
public GlideOptions encodeFormat(@NonNull Bitmap.CompressFormat format) {
return (GlideOptions) super.encodeFormat(format);
}
@Override
@NonNull
@CheckResult
public GlideOptions encodeQuality(@IntRange(from = 0, to = 100) int value) {
return (GlideOptions) super.encodeQuality(value);
}
@Override
@NonNull
@CheckResult
public GlideOptions frame(@IntRange(from = 0) long value) {
return (GlideOptions) super.frame(value);
}
@Override
@NonNull
@CheckResult
public GlideOptions format(@NonNull DecodeFormat format) {
return (GlideOptions) super.format(format);
}
@Override
@NonNull
@CheckResult
public GlideOptions disallowHardwareConfig() {
return (GlideOptions) super.disallowHardwareConfig();
}
@Override
@NonNull
@CheckResult
public GlideOptions downsample(@NonNull DownsampleStrategy strategy) {
return (GlideOptions) super.downsample(strategy);
}
@Override
@NonNull
@CheckResult
public GlideOptions timeout(@IntRange(from = 0) int value) {
return (GlideOptions) super.timeout(value);
}
@Override
@NonNull
@CheckResult
public GlideOptions optionalCenterCrop() {
return (GlideOptions) super.optionalCenterCrop();
}
@Override
@NonNull
@CheckResult
public GlideOptions centerCrop() {
return (GlideOptions) super.centerCrop();
}
@Override
@NonNull
@CheckResult
public GlideOptions optionalFitCenter() {
return (GlideOptions) super.optionalFitCenter();
}
@Override
@NonNull
@CheckResult
public GlideOptions fitCenter() {
return (GlideOptions) super.fitCenter();
}
@Override
@NonNull
@CheckResult
public GlideOptions optionalCenterInside() {
return (GlideOptions) super.optionalCenterInside();
}
@Override
@NonNull
@CheckResult
public GlideOptions centerInside() {
return (GlideOptions) super.centerInside();
}
@Override
@NonNull
@CheckResult
public GlideOptions optionalCircleCrop() {
return (GlideOptions) super.optionalCircleCrop();
}
@Override
@NonNull
@CheckResult
public GlideOptions circleCrop() {
return (GlideOptions) super.circleCrop();
}
@Override
@NonNull
@CheckResult
public GlideOptions transform(@NonNull Transformation transformation) {
return (GlideOptions) super.transform(transformation);
}
@Override
@SafeVarargs
@SuppressWarnings("varargs")
@NonNull
@CheckResult
public final GlideOptions transform(@NonNull Transformation... transformations) {
return (GlideOptions) super.transform(transformations);
}
@Override
@SafeVarargs
@SuppressWarnings("varargs")
@Deprecated
@NonNull
@CheckResult
public final GlideOptions transforms(@NonNull Transformation... transformations) {
return (GlideOptions) super.transforms(transformations);
}
@Override
@NonNull
@CheckResult
public GlideOptions optionalTransform(@NonNull Transformation transformation) {
return (GlideOptions) super.optionalTransform(transformation);
}
@Override
@NonNull
@CheckResult
public GlideOptions optionalTransform(@NonNull Class clazz,
@NonNull Transformation transformation) {
return (GlideOptions) super.optionalTransform(clazz, transformation);
}
@Override
@NonNull
@CheckResult
public GlideOptions transform(@NonNull Class clazz,
@NonNull Transformation transformation) {
return (GlideOptions) super.transform(clazz, transformation);
}
@Override
@NonNull
@CheckResult
public GlideOptions dontTransform() {
return (GlideOptions) super.dontTransform();
}
@Override
@NonNull
@CheckResult
public GlideOptions dontAnimate() {
return (GlideOptions) super.dontAnimate();
}
@Override
@NonNull
@CheckResult
public GlideOptions apply(@NonNull BaseRequestOptions> options) {
return (GlideOptions) super.apply(options);
}
@Override
@NonNull
public GlideOptions lock() {
return (GlideOptions) super.lock();
}
@Override
@NonNull
public GlideOptions autoClone() {
return (GlideOptions) super.autoClone();
}
}
================================================
FILE: annotation/compiler/test/src/test/resources/EmptyAppGlideModuleTest/GlideRequest.java
================================================
package com.bumptech.glide.test;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import androidx.annotation.CheckResult;
import androidx.annotation.DrawableRes;
import androidx.annotation.FloatRange;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RawRes;
import com.bumptech.glide.Glide;
import com.bumptech.glide.Priority;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.TransitionOptions;
import com.bumptech.glide.load.DecodeFormat;
import com.bumptech.glide.load.Key;
import com.bumptech.glide.load.Option;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
import com.bumptech.glide.request.BaseRequestOptions;
import com.bumptech.glide.request.RequestListener;
import java.io.File;
import java.net.URL;
import java.util.List;
/**
* Contains all public methods from {@link RequestBuilder}, all options from
* {@link com.bumptech.glide.request.RequestOptions} and all generated options from
* {@link com.bumptech.glide.annotation.GlideOption} in annotated methods in
* {@link com.bumptech.glide.annotation.GlideExtension} annotated classes.
*
* Generated code, do not modify.
*
* @see RequestBuilder
* @see com.bumptech.glide.request.RequestOptions
*/
@SuppressWarnings({
"unused",
"deprecation"
})
public class GlideRequest extends RequestBuilder implements Cloneable {
GlideRequest(@NonNull Class transcodeClass, @NonNull RequestBuilder> other) {
super(transcodeClass, other);
}
GlideRequest(@NonNull Glide glide, @NonNull RequestManager requestManager,
@NonNull Class transcodeClass, @NonNull Context context) {
super(glide, requestManager ,transcodeClass, context);
}
@Override
@CheckResult
@NonNull
protected GlideRequest getDownloadOnlyRequest() {
return new GlideRequest<>(File.class, this).apply(DOWNLOAD_ONLY_OPTIONS);
}
/**
* @see GlideOptions#sizeMultiplier(float)
*/
@NonNull
@CheckResult
public GlideRequest sizeMultiplier(@FloatRange(from = 0.0, to = 1.0) float value) {
return (GlideRequest) super.sizeMultiplier(value);
}
/**
* @see GlideOptions#useUnlimitedSourceGeneratorsPool(boolean)
*/
@NonNull
@CheckResult
public GlideRequest useUnlimitedSourceGeneratorsPool(boolean flag) {
return (GlideRequest) super.useUnlimitedSourceGeneratorsPool(flag);
}
/**
* @see GlideOptions#useAnimationPool(boolean)
*/
@NonNull
@CheckResult
public GlideRequest useAnimationPool(boolean flag) {
return (GlideRequest) super.useAnimationPool(flag);
}
/**
* @see GlideOptions#onlyRetrieveFromCache(boolean)
*/
@NonNull
@CheckResult
public GlideRequest onlyRetrieveFromCache(boolean flag) {
return (GlideRequest) super.onlyRetrieveFromCache(flag);
}
/**
* @see GlideOptions#diskCacheStrategy(DiskCacheStrategy)
*/
@NonNull
@CheckResult
public GlideRequest diskCacheStrategy(@NonNull DiskCacheStrategy strategy) {
return (GlideRequest) super.diskCacheStrategy(strategy);
}
/**
* @see GlideOptions#priority(Priority)
*/
@NonNull
@CheckResult
public GlideRequest priority(@NonNull Priority priority) {
return (GlideRequest) super.priority(priority);
}
/**
* @see GlideOptions#placeholder(Drawable)
*/
@NonNull
@CheckResult
public GlideRequest placeholder(@Nullable Drawable drawable) {
return (GlideRequest) super.placeholder(drawable);
}
/**
* @see GlideOptions#placeholder(int)
*/
@NonNull
@CheckResult
public GlideRequest placeholder(@DrawableRes int id) {
return (GlideRequest) super.placeholder(id);
}
/**
* @see GlideOptions#fallback(Drawable)
*/
@NonNull
@CheckResult
public GlideRequest fallback(@Nullable Drawable drawable) {
return (GlideRequest) super.fallback(drawable);
}
/**
* @see GlideOptions#fallback(int)
*/
@NonNull
@CheckResult
public GlideRequest fallback(@DrawableRes int id) {
return (GlideRequest) super.fallback(id);
}
/**
* @see GlideOptions#error(Drawable)
*/
@NonNull
@CheckResult
public GlideRequest error(@Nullable Drawable drawable) {
return (GlideRequest) super.error(drawable);
}
/**
* @see GlideOptions#error(int)
*/
@NonNull
@CheckResult
public GlideRequest error(@DrawableRes int id) {
return (GlideRequest) super.error(id);
}
/**
* @see GlideOptions#theme(Resources.Theme)
*/
@NonNull
@CheckResult
public GlideRequest theme(@Nullable Resources.Theme theme) {
return (GlideRequest) super.theme(theme);
}
/**
* @see GlideOptions#skipMemoryCache(boolean)
*/
@NonNull
@CheckResult
public GlideRequest skipMemoryCache(boolean skip) {
return (GlideRequest) super.skipMemoryCache(skip);
}
/**
* @see GlideOptions#override(int, int)
*/
@NonNull
@CheckResult
public GlideRequest override(int width, int height) {
return (GlideRequest