/** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.litho.specmodels.generator; import javax.lang.model.element.Modifier; import java.lang.annotation.Annotation; import com.facebook.litho.annotations.ShouldUpdate; import com.facebook.litho.specmodels.model.DelegateMethodModel; import com.facebook.litho.specmodels.model.HasPureRender; import com.facebook.litho.specmodels.model.MethodParamModel; import com.facebook.litho.specmodels.model.SpecModel; import com.facebook.litho.specmodels.model.SpecModelUtils; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeName; /** * Class that generates the pure render methods for a Component. */ public class PureRenderGenerator { private PureRenderGenerator() { } public static <S extends SpecModel & HasPureRender> TypeSpecDataHolder generate(S specModel) { TypeSpecDataHolder.Builder dataHolder = TypeSpecDataHolder.newBuilder(); if (specModel.isPureRender()) { dataHolder.addTypeSpecDataHolder(generateIsPureRender()); dataHolder.addTypeSpecDataHolder(generateShouldUpdateMethod(specModel)); } return dataHolder.build(); } static TypeSpecDataHolder generateIsPureRender() { return TypeSpecDataHolder.newBuilder().addMethod( MethodSpec.methodBuilder("isPureRender") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(TypeName.BOOLEAN) .addStatement("return true") .build()) .build(); } static TypeSpecDataHolder generateShouldUpdateMethod(SpecModel specModel) { TypeSpecDataHolder.Builder dataHolder = TypeSpecDataHolder.newBuilder(); final DelegateMethodModel methodModel = SpecModelUtils.getMethodModelWithAnnotation(specModel, ShouldUpdate.class); if (methodModel == null) { return dataHolder.build(); } final ShouldUpdate annotation = getShouldUpdateAnnotation(methodModel); if (annotation.onMount()) { dataHolder.addMethod( MethodSpec.methodBuilder("callsShouldUpdateOnMount") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(TypeName.BOOLEAN) .addStatement("return true") .build()); } final MethodSpec.Builder shouldUpdateComponent = MethodSpec.methodBuilder("shouldUpdate") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(TypeName.BOOLEAN) .addParameter(specModel.getComponentClass(), "previous") .addParameter(specModel.getComponentClass(), "next"); if (methodModel.methodParams.size() > 0) { shouldUpdateComponent .addStatement( "$L previousImpl = ($L) previous", ComponentImplGenerator.getImplClassName(specModel), ComponentImplGenerator.getImplClassName(specModel)) .addStatement( "$L nextImpl = ($L) next", ComponentImplGenerator.getImplClassName(specModel), ComponentImplGenerator.getImplClassName(specModel)); } final CodeBlock.Builder delegateParameters = CodeBlock.builder(); delegateParameters.indent(); final CodeBlock.Builder releaseDiffs = CodeBlock.builder(); for (int i = 0, size = methodModel.methodParams.size(); i < size; i++) { MethodParamModel methodParam = methodModel.methodParams.get(i); shouldUpdateComponent .addStatement( "$T $L = acquireDiff(previousImpl.$L, nextImpl.$L)", methodParam.getType(), methodParam.getName(), methodParam.getName(), methodParam.getName()); if (i != 0) { delegateParameters.add(",\n"); } delegateParameters.add(methodParam.getName()); releaseDiffs.addStatement("releaseDiff($L)", methodParam.getName()); } delegateParameters.unindent(); shouldUpdateComponent.addStatement( "boolean shouldUpdate = $L.$L($L)", SpecModelUtils.getSpecAccessor(specModel), methodModel.name, delegateParameters.build()); shouldUpdateComponent.addCode(releaseDiffs.build()); shouldUpdateComponent.addStatement("return shouldUpdate"); return dataHolder.addMethod(shouldUpdateComponent.build()).build(); } static ShouldUpdate getShouldUpdateAnnotation(DelegateMethodModel delegateMethodModel) { for (Annotation annotation : delegateMethodModel.annotations) { if (annotation.annotationType().equals(ShouldUpdate.class)) { return (ShouldUpdate) annotation; } } throw new RuntimeException("Expected to find a ShouldUpdate annotation"); } }