package com.mutualmobile.barricade.compiler; import com.mutualmobile.barricade.IBarricadeConfig; import com.mutualmobile.barricade.response.BarricadeResponse; import com.mutualmobile.barricade.response.BarricadeResponseSet; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeSpec; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.tools.Diagnostic; import static com.squareup.javapoet.JavaFile.builder; import static com.squareup.javapoet.TypeSpec.classBuilder; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.STATIC; /** * Generates code for a Barricade configuration. */ final class CodeGenerator { private static final String CLASS_NAME = "BarricadeConfig"; private static final String ENDPOINTS_CLASS_NAME = "Endpoints"; private static final String RESPONSES_CLASS_NAME = "Responses"; private static final String PACKAGE_NAME = "com.mutualmobile.barricade"; private static final ClassName TYPE_BARRICADE_RESPONSE_SET = ClassName.get(BarricadeResponseSet.class); private static final ParameterizedTypeName TYPE_CONFIG = ParameterizedTypeName.get(ClassName.get(HashMap.class), ClassName.get(String.class), ClassName.get(BarricadeResponseSet.class)); private CodeGenerator() { } /** * Generates the code for the Barricade configuration based on the annotations found. * * @param processingEnv Processing environment * @param configs Configuration detected by annotation processing * @param messager Messager to print logs * @throws IOException */ static void generateClass(ProcessingEnvironment processingEnv, HashMap<String, BarricadeResponseSet> configs, Messager messager) throws IOException { messager.printMessage(Diagnostic.Kind.NOTE, "Generating configuration code..."); TypeSpec.Builder classBuilder = classBuilder(CLASS_NAME).addModifiers(PUBLIC, FINAL); FieldSpec valuesField = FieldSpec.builder(TYPE_CONFIG, "configs").addModifiers(PRIVATE).build(); FieldSpec instanceField = FieldSpec.builder(ClassName.get(PACKAGE_NAME, CLASS_NAME), "barricadeConfig") .addModifiers(PRIVATE, STATIC) .build(); MethodSpec.Builder instanceMethodBuilder = generateGetInstanceMethodBuilder(); MethodSpec.Builder constructorMethodBuilder = generateConstructorBuilder(configs, messager); MethodSpec.Builder valuesMethod = generateGetConfigsMethodBuilder(); MethodSpec.Builder getResponseMethodBuilder = generateGetResponseMethodBuilder(); classBuilder.addType(generateEndpointsInnerClass(configs.keySet())); classBuilder.addType(generateResponsesInnerClass(configs)); classBuilder.addField(instanceField); classBuilder.addField(valuesField); classBuilder.addMethod(instanceMethodBuilder.build()); classBuilder.addMethod(constructorMethodBuilder.build()); classBuilder.addMethod(valuesMethod.build()); classBuilder.addMethod(getResponseMethodBuilder.build()); classBuilder.addSuperinterface(IBarricadeConfig.class); JavaFile.Builder javaFileBuilder = builder(PACKAGE_NAME, classBuilder.build()); JavaFile javaFile = javaFileBuilder.build(); javaFile.writeTo(processingEnv.getFiler()); messager.printMessage(Diagnostic.Kind.NOTE, "Code generation complete!"); } private static TypeSpec generateEndpointsInnerClass(Set<String> endPoints) { TypeSpec.Builder classBuilder = classBuilder(ENDPOINTS_CLASS_NAME).addModifiers(PUBLIC, STATIC, FINAL); for (String endPoint : endPoints) { FieldSpec valuesField = FieldSpec.builder(String.class, StringUtils.removeAllSpecialCharacters(endPoint).toUpperCase()) .addModifiers(PUBLIC, STATIC, FINAL) .initializer("$S", endPoint) .build(); classBuilder.addField(valuesField); } return classBuilder.build(); } private static TypeSpec generateResponsesInnerClass( HashMap<String, BarricadeResponseSet> configs) { TypeSpec.Builder classBuilder = classBuilder(RESPONSES_CLASS_NAME).addModifiers(PUBLIC, STATIC, FINAL); for (String endpoint : configs.keySet()) { classBuilder.addType( generateEndpointsResponsesInnerClass(endpoint, configs.get(endpoint).responses)); } return classBuilder.build(); } private static TypeSpec generateEndpointsResponsesInnerClass(String endpoint, List<BarricadeResponse> responses) { TypeSpec.Builder classBuilder = classBuilder(StringUtils.toCamelCase(endpoint)).addModifiers(PUBLIC, STATIC, FINAL); int count = 0; for (BarricadeResponse response : responses) { FieldSpec valuesField = FieldSpec.builder(int.class, StringUtils.removeAllSpecialCharactersAndExtensions(response.responseFileName).toUpperCase()) .addModifiers(PUBLIC, STATIC, FINAL) .initializer("$L", count) .build(); classBuilder.addField(valuesField); count++; } return classBuilder.build(); } private static MethodSpec.Builder generateGetConfigsMethodBuilder() { return MethodSpec.methodBuilder("getConfigs") .returns(TYPE_CONFIG) .addModifiers(PUBLIC) .addStatement("return configs"); } private static MethodSpec.Builder generateConstructorBuilder( HashMap<String, BarricadeResponseSet> values, Messager messager) { MethodSpec.Builder methodBuilder = MethodSpec.constructorBuilder().addModifiers(PUBLIC); methodBuilder.addStatement("configs = new HashMap<>()"); for (Map.Entry<String, BarricadeResponseSet> entry : values.entrySet()) { BarricadeResponseSet barricadeResponseSet = entry.getValue(); String listName = "barricadeResponsesFor" + entry.getKey(); methodBuilder.addStatement("$T<$T> " + listName + " = new $T<>()", List.class, BarricadeResponse.class, ArrayList.class); for (BarricadeResponse barricadeResponse : barricadeResponseSet.responses) { methodBuilder.addStatement(listName + ".add(new $T($L, $S, $S))", BarricadeResponse.class, barricadeResponse.statusCode, barricadeResponse.responseFileName, barricadeResponse.contentType); } methodBuilder.addStatement( "configs.put($S, new $T(" + listName + ", " + barricadeResponseSet.defaultIndex + "))", entry.getKey(), TYPE_BARRICADE_RESPONSE_SET); } return methodBuilder; } private static MethodSpec.Builder generateGetInstanceMethodBuilder() { return MethodSpec.methodBuilder("getInstance") .returns(ClassName.get(PACKAGE_NAME, CLASS_NAME)) .addModifiers(PUBLIC, STATIC) .addStatement("return barricadeConfig = barricadeConfig != null? barricadeConfig:" + " new BarricadeConfig()"); } private static MethodSpec.Builder generateGetResponseMethodBuilder() { return MethodSpec.methodBuilder("getResponseForEndpoint") .addModifiers(PUBLIC) .addParameter(String.class, "endpoint") .returns(BarricadeResponse.class) .addStatement("$T responseSet = configs.get(endpoint)", BarricadeResponseSet.class) .addStatement("if(responseSet==null) return null") .addStatement("return responseSet.responses.get(responseSet.defaultIndex)"); } }