Skip to content

Commit

Permalink
Directly include RequestOptions in ReqestBuilder using CRGP
Browse files Browse the repository at this point in the history
https://www.artima.com/weblogs/viewpost.jsp?thread=133275

This means that you can write:

Glide.with(context)
  .load(url)
  .placeholder(R.drawable.placeholder)
  .into(imageView);

Instead of:

Glide.with(contex)
  .apply(placeholderOf(R.drawable.placeholder)
  .into(imageView);

Without using the generated API.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=211640993
  • Loading branch information
sjudd committed Sep 6, 2018
1 parent 092e062 commit ed20643
Show file tree
Hide file tree
Showing 42 changed files with 3,890 additions and 8,010 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ boolean maybeWriteAppModule() {
writeRequestOptions(generatedCodePackageName, generatedRequestOptions);

TypeSpec generatedRequestBuilder =
requestBuilderGenerator.generate(generatedCodePackageName, generatedRequestOptions);
requestBuilderGenerator.generate(generatedCodePackageName, indexedClassNames.extensions);
writeRequestBuilder(generatedCodePackageName, generatedRequestBuilder);

TypeSpec requestManager =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.bumptech.glide.annotation.compiler;

/**
* Generates unique field ids for classes generated by Glide's annotation processor.
*/
final class FieldUniqueIdGenerator {
private static int nextStaticFieldUniqueId;

private FieldUniqueIdGenerator() {
// Utility class.
}

static int next() {
return nextStaticFieldUniqueId++;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,12 @@ private void validateNewGlideOption(ExecutableElement executableElement) {
validateNewGlideOptionAnnotations(executableElement);
validateGlideOptionParameters(executableElement);
TypeMirror returnType = executableElement.getReturnType();
if (!isRequestOptions(returnType)) {
throw new IllegalArgumentException("@GlideOption methods should return a RequestOptions"
+ " 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");
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);
}
Expand All @@ -122,45 +123,45 @@ private void validateDeprecatedGlideOption(ExecutableElement executableElement)
private static void validateGlideOptionParameters(ExecutableElement executableElement) {
if (executableElement.getParameters().isEmpty()) {
throw new IllegalArgumentException("@GlideOption methods must take a "
+ "RequestOptions object as their first parameter, but "
+ "BaseRequestOptions<?> object as their first parameter, but "
+ getQualifiedMethodName(executableElement) + " has none");
}
VariableElement first = executableElement.getParameters().get(0);
TypeMirror expected = first.asType();
if (!isRequestOptions(expected)) {
if (!isBaseRequestOptions(expected)) {
throw new IllegalArgumentException("@GlideOption methods must take a"
+ " RequestOptions object as their first parameter, but the first parameter in "
+ " BaseRequestOptions<?> object as their first parameter, but the first parameter in "
+ getQualifiedMethodName(executableElement) + " is " + expected);
}
}

private static boolean isRequestOptions(TypeMirror typeMirror) {
return typeMirror.toString().equals("com.bumptech.glide.request.RequestOptions");
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 isOverridingRequestOptionsMethod = isMethodInRequestOptions(element);
if (isOverridingRequestOptionsMethod && overrideType == GlideOption.OVERRIDE_NONE) {
boolean isOverridingBaseRequestOptionsMethod = isMethodInBaseRequestOptions(element);
if (isOverridingBaseRequestOptionsMethod && overrideType == GlideOption.OVERRIDE_NONE) {
throw new IllegalArgumentException("Accidentally attempting to override a method in"
+ " RequestOptions. Add an 'override' value in the @GlideOption annotation"
+ " BaseRequestOptions. Add an 'override' value in the @GlideOption annotation"
+ " if this is intentional. Offending method: "
+ getQualifiedMethodName(element));
} else if (!isOverridingRequestOptionsMethod && overrideType != GlideOption.OVERRIDE_NONE) {
} else if (!isOverridingBaseRequestOptionsMethod && overrideType != GlideOption.OVERRIDE_NONE) {
throw new IllegalArgumentException("Requested to override an existing method in"
+ " RequestOptions, but no such method was found. Offending method: "
+ " BaseRequestOptions, but no such method was found. Offending method: "
+ getQualifiedMethodName(element));
}
}

private boolean isMethodInRequestOptions(ExecutableElement toFind) {
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.REQUEST_OPTIONS_QUALIFIED_NAME);
.getTypeElement(RequestOptionsGenerator.BASE_REQUEST_OPTIONS_QUALIFIED_NAME);
List<String> toFindParameterNames = getComparableParameterNames(toFind, true /*skipFirst*/);
String toFindSimpleName = toFind.getSimpleName().toString();
for (Element element : requestOptionsType.getEnclosedElements()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
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;
Expand All @@ -23,12 +22,8 @@
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;
Expand Down Expand Up @@ -113,12 +108,11 @@ final class RequestBuilderGenerator {

private final ProcessingEnvironment processingEnv;
private final ProcessorUtil processorUtil;
private ClassName generatedRequestBuilderClassName;
private final RequestOptionsOverrideGenerator requestOptionsOverrideGenerator;
private final TypeVariableName transcodeTypeName;
private ParameterizedTypeName generatedRequestBuilderOfTranscodeType;
private final TypeElement requestOptionsType;
private final TypeElement requestBuilderType;
private ClassName requestOptionsClassName;
private ClassName generatedRequestBuilderClassName;

RequestBuilderGenerator(ProcessingEnvironment processingEnv, ProcessorUtil processorUtil) {
this.processingEnv = processingEnv;
Expand All @@ -131,29 +125,28 @@ final class RequestBuilderGenerator {

requestOptionsType = processingEnv.getElementUtils().getTypeElement(
REQUEST_OPTIONS_QUALIFIED_NAME);

requestOptionsOverrideGenerator =
new RequestOptionsOverrideGenerator(processingEnv, processorUtil);
}

TypeSpec generate(String generatedCodePackageName, @Nullable TypeSpec generatedOptions) {
TypeSpec generate(String generatedCodePackageName, Set<String> glideExtensionClassNames) {
generatedRequestBuilderClassName =
ClassName.get(generatedCodePackageName, GENERATED_REQUEST_BUILDER_SIMPLE_NAME);
generatedRequestBuilderOfTranscodeType =
ParameterizedTypeName generatedRequestBuilderOfTranscodeType =
ParameterizedTypeName.get(generatedRequestBuilderClassName, transcodeTypeName);

if (generatedOptions != null) {
requestOptionsClassName =
ClassName.get(generatedCodePackageName, generatedOptions.name);
} else {
requestOptionsClassName =
ClassName.get(
RequestOptionsGenerator.REQUEST_OPTIONS_PACKAGE_NAME,
RequestBuilderGenerator.REQUEST_OPTIONS_SIMPLE_NAME);
}
RequestOptionsExtensionGenerator requestOptionsExtensionGenerator =
new RequestOptionsExtensionGenerator(generatedRequestBuilderOfTranscodeType, processorUtil);

ParameterizedTypeName requestBuilderOfTranscodeType =
ParameterizedTypeName.get(
ClassName.get(REQUEST_BUILDER_PACKAGE_NAME, REQUEST_BUILDER_SIMPLE_NAME),
transcodeTypeName);

List<MethodSpec> 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)
Expand All @@ -176,8 +169,11 @@ TypeSpec generate(String generatedCodePackageName, @Nullable TypeSpec generatedO
.addSuperinterface(Cloneable.class)
.addMethods(generateConstructors())
.addMethod(generateDownloadOnlyRequestMethod())
.addMethods(generateGeneratedRequestOptionsEquivalents(generatedOptions))
.addMethods(
requestOptionsOverrideGenerator.generateInstanceMethodOverridesForRequestOptions(
generatedRequestBuilderOfTranscodeType, EXCLUDED_METHODS_FROM_BASE_REQUEST_OPTIONS))
.addMethods(generateRequestBuilderOverrides())
.addMethods(requestOptionsExtensionMethods)
.build();
}

Expand All @@ -198,6 +194,7 @@ public MethodSpec apply(ExecutableElement 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
Expand Down Expand Up @@ -245,152 +242,6 @@ public String apply(ParameterSpec input) {
return builder.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<MethodSpec> generateGeneratedRequestOptionsEquivalents(
@Nullable final TypeSpec generatedOptions) {
if (generatedOptions == null) {
return Collections.emptyList();
}
return FluentIterable
.from(generatedOptions.methodSpecs)
.filter(new Predicate<MethodSpec>() {
@Override
public boolean apply(MethodSpec input) {
return isUsefulGeneratedRequestOption(input);
}
})
.transform(new Function<MethodSpec, MethodSpec>() {
@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.
*
* <p>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(MethodSpec requestOptionMethod) {
return
!EXCLUDED_METHODS_FROM_BASE_REQUEST_OPTIONS.contains(requestOptionMethod.name)
&& requestOptionMethod.hasModifier(Modifier.PUBLIC)
&& !requestOptionMethod.hasModifier(Modifier.STATIC)
&& requestOptionMethod.returnType.toString()
.equals(requestOptionsClassName.toString());
}

/**
* 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<ParameterSpec, String>() {
@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<AnnotationSpec>() {
@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)
.beginControlFlow(
"if (getMutableOptions() instanceof $T)", requestOptionsClassName)
.addCode("this.requestOptions = (($T) getMutableOptions())",
requestOptionsClassName)
.addCode(callRequestOptionsMethod)
.nextControlFlow("else")
.addCode(CodeBlock.of("this.requestOptions = new $T().apply(this.requestOptions)",
requestOptionsClassName))
.addCode(callRequestOptionsMethod)
.endControlFlow()
.addStatement("return this");

AnnotationSpec suppressWarnings = buildSuppressWarnings(requestOptionMethod);
if (suppressWarnings != null) {
result.addAnnotation(suppressWarnings);
}
return result.build();
}

@Nullable
private AnnotationSpec buildSuppressWarnings(MethodSpec requestOptionMethod) {
Set<String> 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<CodeBlock> codeBlocks = annotation.members.get("value");
suppressions.addAll(FluentIterable.from(codeBlocks).transform(
new Function<CodeBlock, String>() {
@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<String> 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();
}

private List<MethodSpec> generateConstructors() {
ParameterizedTypeName classOfTranscodeType =
ParameterizedTypeName.get(ClassName.get(Class.class), transcodeTypeName);
Expand Down
Loading

0 comments on commit ed20643

Please sign in to comment.