package com.arellomobile.mvp.compiler.viewstate; import com.arellomobile.mvp.MvpProcessor; import com.arellomobile.mvp.compiler.JavaFilesGenerator; import com.arellomobile.mvp.viewstate.MvpViewState; import com.arellomobile.mvp.viewstate.ViewCommand; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.JavaFile; 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 java.util.Collections; import java.util.List; import java.util.Random; import javax.lang.model.element.Modifier; import static com.arellomobile.mvp.compiler.Util.decapitalizeString; /** * Date: 18.12.2015 * Time: 13:24 * * @author Yuri Shmakov */ public final class ViewStateClassGenerator extends JavaFilesGenerator<ViewInterfaceInfo> { @Override public List<JavaFile> generate(ViewInterfaceInfo viewInterfaceInfo) { ClassName viewName = viewInterfaceInfo.getName(); TypeName nameWithTypeVariables = viewInterfaceInfo.getNameWithTypeVariables(); TypeSpec.Builder classBuilder = TypeSpec.classBuilder(viewName.simpleName() + MvpProcessor.VIEW_STATE_SUFFIX) .addModifiers(Modifier.PUBLIC) .superclass(ParameterizedTypeName.get(ClassName.get(MvpViewState.class), nameWithTypeVariables)) .addSuperinterface(nameWithTypeVariables) .addTypeVariables(viewInterfaceInfo.getTypeVariables()); for (ViewMethod method : viewInterfaceInfo.getMethods()) { TypeSpec commandClass = generateCommandClass(method, nameWithTypeVariables); classBuilder.addType(commandClass); classBuilder.addMethod(generateMethod(method, nameWithTypeVariables, commandClass)); } JavaFile javaFile = JavaFile.builder(viewName.packageName(), classBuilder.build()) .indent("\t") .build(); return Collections.singletonList(javaFile); } private TypeSpec generateCommandClass(ViewMethod method, TypeName viewTypeName) { MethodSpec applyMethod = MethodSpec.methodBuilder("apply") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .addParameter(viewTypeName, "mvpView") .addExceptions(method.getExceptions()) .addStatement("mvpView.$L($L)", method.getName(), method.getArgumentsString()) .build(); TypeSpec.Builder classBuilder = TypeSpec.classBuilder(method.getCommandClassName()) .addModifiers(Modifier.PUBLIC) // TODO: private and static .addTypeVariables(method.getTypeVariables()) .superclass(ParameterizedTypeName.get(ClassName.get(ViewCommand.class), viewTypeName)) .addMethod(generateCommandConstructor(method)) .addMethod(applyMethod); for (ParameterSpec parameter : method.getParameterSpecs()) { // TODO: private field classBuilder.addField(parameter.type, parameter.name, Modifier.PUBLIC, Modifier.FINAL); } return classBuilder.build(); } private MethodSpec generateMethod(ViewMethod method, TypeName viewTypeName, TypeSpec commandClass) { // TODO: String commandFieldName = "$cmd"; String commandFieldName = decapitalizeString(method.getCommandClassName()); // Add salt if contains argument with same name Random random = new Random(); while (method.getArgumentsString().contains(commandFieldName)) { commandFieldName += random.nextInt(10); } return MethodSpec.overriding(method.getElement()) .addStatement("$1N $2L = new $1N($3L)", commandClass, commandFieldName, method.getArgumentsString()) .addStatement("mViewCommands.beforeApply($L)", commandFieldName) .addCode("\n") .beginControlFlow("if (mViews == null || mViews.isEmpty())") .addStatement("return") .endControlFlow() .addCode("\n") .beginControlFlow("for ($T view : mViews)", viewTypeName) .addStatement("view.$L($L)", method.getName(), method.getArgumentsString()) .endControlFlow() .addCode("\n") .addStatement("mViewCommands.afterApply($L)", commandFieldName) .build(); } private MethodSpec generateCommandConstructor(ViewMethod method) { List<ParameterSpec> parameters = method.getParameterSpecs(); MethodSpec.Builder builder = MethodSpec.constructorBuilder() .addParameters(parameters) .addStatement("super($S, $T.class)", method.getTag(), method.getStrategy()); if (parameters.size() > 0) { builder.addCode("\n"); } for (ParameterSpec parameter : parameters) { builder.addStatement("this.$1N = $1N", parameter); } return builder.build(); } }