package io.kaif.mobile.retrofit.processor; 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.List; import java.util.Optional; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import retrofit2.http.GET; import retrofit2.http.Headers; import rx.Observable; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toList; public class RetrofitServiceMethod { private final ExecutableElement methodElement; private static final String CACHE_STALE_HEADER = "Cache-Control: max-stale=86400"; public RetrofitServiceMethod(ExecutableElement methodElement) { this.methodElement = methodElement; } public List<MethodSpec> generateCodeWithRetryStaleIfRequired() { MethodSpec methodSpec = generateCode(false); if (!canAppendStaleHeader()) { return singletonList(methodSpec); } return asList(methodSpec, generateCode(true)); } private MethodSpec generateCode(boolean withRetryStaleHeader) { MethodSpec.Builder builder = MethodSpec.methodBuilder(getMethodName(withRetryStaleHeader)); builder.addModifiers(Modifier.ABSTRACT).addModifiers(Modifier.PUBLIC); methodElement.getParameters().stream().map(variableElement -> { ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(TypeName.get(variableElement.asType()), variableElement.getSimpleName().toString()); variableElement.getAnnotationMirrors() .stream() .map(AnnotationSpecUtil::generate) .forEach(parameterBuilder::addAnnotation); return parameterBuilder.build(); }).forEach(builder::addParameter); List<AnnotationSpec> annotationSpecs = methodElement.getAnnotationMirrors() .stream() .map(AnnotationSpecUtil::generate) .collect(toList()); if (withRetryStaleHeader) { Optional<AnnotationSpec> header = annotationSpecs.stream() .filter(annotationSpec -> isHeaderAnnotation(annotationSpec)) .findAny(); if (header.isPresent()) { annotationSpecs.forEach(annotationSpec -> { if (isHeaderAnnotation(annotationSpec)) { AnnotationSpec.Builder replace = AnnotationSpec.builder((ClassName) annotationSpec.type); annotationSpec.members.forEach((String s, List<CodeBlock> codeBlocks) -> { codeBlocks.forEach(codeBlock -> { replace.addMember(s, codeBlock); }); }); replace.addMember("value", "$S", CACHE_STALE_HEADER); builder.addAnnotation(replace.build()); } else { builder.addAnnotation(annotationSpec); } }); } else { annotationSpecs.forEach(builder::addAnnotation); builder.addAnnotation(AnnotationSpec.builder(Headers.class) .addMember("value", "$S", CACHE_STALE_HEADER) .build()); } } else { annotationSpecs.forEach(builder::addAnnotation); } return builder.returns(TypeName.get(methodElement.getReturnType())).build(); } private String getMethodName(boolean withRetryStaleHeader) { return methodElement.getSimpleName().toString() + (withRetryStaleHeader ? "$$RetryStale" : ""); } private boolean canAppendStaleHeader() { if (methodElement.getAnnotation(GET.class) == null) { return false; } if (methodElement.getReturnType().getKind() != TypeKind.DECLARED) { return false; } String rawName = ((DeclaredType) methodElement.getReturnType()).asElement().toString(); return rawName.equals(Observable.class.getCanonicalName()); } private static boolean isHeaderAnnotation(AnnotationSpec annotationSpec) { return annotationSpec.type.toString().equals(Headers.class.getCanonicalName()); } }