From c91d2518b8aa7235883aa87ed13fa0dcf6f4b71d Mon Sep 17 00:00:00 2001 From: Eli Hart Date: Tue, 10 Oct 2017 22:21:37 -0700 Subject: [PATCH] Make generated models from views inherit constructors from base model --- build.gradle | 2 +- .../java/com/airbnb/epoxy/EpoxyModel.java | 11 + .../airbnb/epoxy/BasicGeneratedModelInfo.java | 55 ++--- .../com/airbnb/epoxy/GeneratedModelInfo.java | 23 +- .../java/com/airbnb/epoxy/ModelViewInfo.java | 205 ------------------ .../java/com/airbnb/epoxy/ModelViewInfo.kt | 169 +++++++++++++++ .../com/airbnb/epoxy/ModelViewProcessor.kt | 2 +- .../java/com/airbnb/epoxy/ModelViewWriter.kt | 4 +- .../com/airbnb/epoxy/ViewAttributeInfo.java | 16 +- .../com/airbnb/epoxy/ViewProcessorTest.kt | 1 + .../BaseModelFromPackageConfigViewModel_.java | 4 + .../test/resources/BaseModelViewModel_.java | 4 + .../BaseModelViewWithSuperDiffBindModel_.java | 4 + .../BaseModelWithAttributeViewModel_.java | 4 + .../TestAfterBindPropsViewModel_.java | 2 +- 15 files changed, 249 insertions(+), 257 deletions(-) delete mode 100644 epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.java create mode 100644 epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.kt diff --git a/build.gradle b/build.gradle index df0f45cba7..38906edbf9 100755 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.KOTLIN_VERSION = "1.1.50" + ext.KOTLIN_VERSION = "1.1.51" ext.ANDROID_PLUGIN_VERSION = "3.0.0-beta7" repositories { diff --git a/epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModel.java b/epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModel.java index a9d036784d..b85679d754 100755 --- a/epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModel.java +++ b/epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModel.java @@ -283,6 +283,17 @@ private static long hashString64Bit(CharSequence str) { return result; } + /** + * Return the default layout resource to be used when creating views for this model. The resource + * will be inflated to create a view for the model; additionally the layout int is used as the + * views type in the RecyclerView. + *

+ * This can be left unimplemented if you use the {@link EpoxyModelClass} annotation to define a + * layout. + *

+ * This default value can be overridden with {@link #layout(int)} at runtime to change the layout + * dynamically. + */ @LayoutRes protected abstract int getDefaultLayout(); diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/BasicGeneratedModelInfo.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/BasicGeneratedModelInfo.java index 213964d6b6..ba10c86250 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/BasicGeneratedModelInfo.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/BasicGeneratedModelInfo.java @@ -9,13 +9,9 @@ import java.util.List; import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; -import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; @@ -23,7 +19,7 @@ import static com.airbnb.epoxy.Utils.getElementByName; import static com.airbnb.epoxy.Utils.getEpoxyObjectType; - class BasicGeneratedModelInfo extends GeneratedModelInfo { +class BasicGeneratedModelInfo extends GeneratedModelInfo { private final Elements elementUtils; @@ -38,7 +34,7 @@ class BasicGeneratedModelInfo extends GeneratedModelInfo { typeVariableNames.add(TypeVariableName.get(typeParameterElement)); } - collectOriginalClassConstructors(superClassElement); + constructors.addAll(getClassConstructors(superClassElement)); collectMethodsReturningClassType(superClassElement, typeUtils); if (!typeVariableNames.isEmpty()) { @@ -83,39 +79,22 @@ protected ClassName buildGeneratedModelName(TypeElement classElement) { return ClassName.get(packageName, className + GENERATED_CLASS_NAME_SUFFIX); } - /** - * Get information about constructors of the original class so we can duplicate them in the - * generated class and call through to super with the proper parameters - */ - private void collectOriginalClassConstructors(TypeElement originalClass) { - for (Element subElement : originalClass.getEnclosedElements()) { - if (subElement.getKind() == ElementKind.CONSTRUCTOR - && !subElement.getModifiers().contains(Modifier.PRIVATE)) { - ExecutableElement castedSubElement = ((ExecutableElement) subElement); - List params = castedSubElement.getParameters(); - constructors - .add(new ConstructorInfo(subElement.getModifiers(), buildParamSpecs(params), - castedSubElement.isVarArgs())); + private void buildAnnotationLists(List annotationMirrors) { + for (AnnotationMirror annotationMirror : annotationMirrors) { + if (!annotationMirror.getElementValues().isEmpty()) { + // Not supporting annotations with values for now + continue; } + + ClassName annotationClass = + ClassName.bestGuess(annotationMirror.getAnnotationType().toString()); + if (annotationClass.equals(ClassName.get(EpoxyModelClass.class))) { + // Don't include our own annotation + continue; + } + + AnnotationSpec annotationSpec = AnnotationSpec.builder(annotationClass).build(); + annotations.add(annotationSpec); } } - - private void buildAnnotationLists(List annotationMirrors) { - for (AnnotationMirror annotationMirror : annotationMirrors) { - if (!annotationMirror.getElementValues().isEmpty()) { - // Not supporting annotations with values for now - continue; - } - - ClassName annotationClass = - ClassName.bestGuess(annotationMirror.getAnnotationType().toString()); - if (annotationClass.equals(ClassName.get(EpoxyModelClass.class))) { - // Don't include our own annotation - continue; - } - - AnnotationSpec annotationSpec = AnnotationSpec.builder(annotationClass).build(); - annotations.add(annotationSpec); - } - } } diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java index acf288ce85..0f8005f53d 100755 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelInfo.java @@ -71,6 +71,27 @@ abstract class GeneratedModelInfo { */ Size layoutParams = Size.NONE; + /** + * Get information about constructors of the original class so we can duplicate them in the + * generated class and call through to super with the proper parameters + */ + protected static List getClassConstructors(TypeElement classElement) { + List constructors = new ArrayList<>(2); + + for (Element subElement : classElement.getEnclosedElements()) { + if (subElement.getKind() == ElementKind.CONSTRUCTOR + && !subElement.getModifiers().contains(Modifier.PRIVATE)) { + + ExecutableElement constructor = ((ExecutableElement) subElement); + List params = constructor.getParameters(); + constructors.add(new ConstructorInfo(subElement.getModifiers(), buildParamSpecs(params), + constructor.isVarArgs())); + } + } + + return constructors; + } + /** * Get information about methods returning class type of the original class so we can duplicate * them in the generated class for chaining purposes @@ -102,7 +123,7 @@ protected void collectMethodsReturningClassType(TypeElement modelClass, Types ty } } - protected List buildParamSpecs(List params) { + protected static List buildParamSpecs(List params) { List result = new ArrayList<>(); for (VariableElement param : params) { diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.java deleted file mode 100644 index 1de0aa2aaa..0000000000 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.airbnb.epoxy; - -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.ParameterizedTypeName; -import com.squareup.javapoet.TypeName; - -import java.util.ArrayList; -import java.util.List; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Modifier; -import javax.lang.model.element.Parameterizable; -import javax.lang.model.element.TypeElement; -import javax.lang.model.element.TypeParameterElement; -import javax.lang.model.type.MirroredTypeException; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; - -import static com.airbnb.epoxy.Utils.isEpoxyModel; - -class ModelViewInfo extends GeneratedModelInfo { - final List resetMethodNames = new ArrayList<>(); - final List afterPropsSetMethodNames = new ArrayList<>(); - final TypeElement viewElement; - final Types typeUtils; - final Elements elements; - final ErrorLogger errorLogger; - final ConfigManager configManager; - private final ResourceProcessor resourceProcessor; - final boolean saveViewState; - final ModelView viewAnnotation; - final boolean fullSpanSize; - - ModelViewInfo(TypeElement viewElement, Types typeUtils, Elements elements, - ErrorLogger errorLogger, ConfigManager configManager, - ResourceProcessor resourceProcessor) { - - viewAnnotation = viewElement.getAnnotation(ModelView.class); - this.viewElement = viewElement; - this.typeUtils = typeUtils; - this.elements = elements; - this.errorLogger = errorLogger; - this.configManager = configManager; - this.resourceProcessor = resourceProcessor; - - superClassElement = lookUpSuperClassElement(); - this.superClassName = ParameterizedTypeName - .get(ClassName.get(superClassElement), TypeName.get(viewElement.asType())); - - generatedClassName = buildGeneratedModelName(viewElement, elements); - // We don't have any type parameters on our generated model - this.parametrizedClassName = generatedClassName; - shouldGenerateModel = !viewElement.getModifiers().contains(Modifier.ABSTRACT); - - collectMethodsReturningClassType(superClassElement, typeUtils); - - // The bound type is the type of this view - boundObjectTypeName = ClassName.get(viewElement.asType()); - - saveViewState = viewAnnotation.saveViewState(); - layoutParams = viewAnnotation.autoLayout(); - fullSpanSize = viewAnnotation.fullSpan(); - includeOtherLayoutOptions = configManager.includeAlternateLayoutsForViews(viewElement); - } - - private TypeElement lookUpSuperClassElement() { - TypeElement defaultSuper = (TypeElement) Utils.getElementByName(ClassNames.EPOXY_MODEL_UNTYPED, - elements, typeUtils); - - // Unfortunately we have to do this weird try/catch to get the class type - TypeMirror classToExtend = null; - try { - viewAnnotation.baseModelClass(); // this should throw - } catch (MirroredTypeException mte) { - classToExtend = mte.getTypeMirror(); - } - - if (classToExtend == null - || classToExtend.toString().equals(Void.class.getCanonicalName())) { - - TypeMirror defaultBaseModel = configManager.getDefaultBaseModel(viewElement); - if (defaultBaseModel != null) { - classToExtend = defaultBaseModel; - } else { - return defaultSuper; - } - } - - if (!isEpoxyModel(classToExtend)) { - errorLogger - .logError("The base model provided to an %s must extend EpoxyModel, but was %s (%s).", - ModelView.class.getSimpleName(), classToExtend, viewElement.getSimpleName()); - return defaultSuper; - } - - if (!validateSuperClassIsTypedCorrectly(classToExtend)) { - errorLogger.logError("The base model provided to an %s must have View as its type (%s).", - ModelView.class.getSimpleName(), viewElement.getSimpleName()); - return defaultSuper; - } - - return (TypeElement) typeUtils.asElement(classToExtend); - } - - /** The super class that our generated model extends from must have View as its only type. */ - private boolean validateSuperClassIsTypedCorrectly(TypeMirror classType) { - Element classElement = typeUtils.asElement(classType); - if (!(classElement instanceof Parameterizable)) { - return false; - } - - Parameterizable parameterizable = (Parameterizable) classElement; - List typeParameters = parameterizable.getTypeParameters(); - if (typeParameters.size() != 1) { - // TODO: (eli_hart 6/15/17) It should be valid to have multiple or no types as long as they - // are correct, but that should be a rare case - return false; - } - - TypeParameterElement typeParam = typeParameters.get(0); - List bounds = typeParam.getBounds(); - if (bounds.isEmpty()) { - // Any type is allowed, so View wil work - return true; - } - - TypeMirror typeMirror = bounds.get(0); - TypeMirror viewType = Utils.getTypeMirror(ClassNames.ANDROID_VIEW, elements, typeUtils); - return typeUtils.isAssignable(viewType, typeMirror) - || typeUtils.isSubtype(typeMirror, viewType); - } - - private ClassName buildGeneratedModelName(TypeElement viewElement, Elements elementUtils) { - String packageName = elementUtils.getPackageOf(viewElement).getQualifiedName().toString(); - - String className = viewElement.getSimpleName().toString(); - className += GENERATED_MODEL_SUFFIX; - - return ClassName.get(packageName, className); - } - - void addProp(ExecutableElement propMethod) { - addAttribute(new ViewAttributeInfo(this, propMethod, typeUtils, elements, errorLogger, - resourceProcessor)); - } - - void addPropIfNotExists(ExecutableElement propMethod) { - addAttributeIfNotExists( - new ViewAttributeInfo(this, propMethod, typeUtils, elements, errorLogger, - resourceProcessor)); - } - - void addOnRecycleMethodIfNotExists(Element resetMethod) { - String methodName = resetMethod.getSimpleName().toString(); - if (!resetMethodNames.contains(methodName)) { - resetMethodNames.add(methodName); - } - } - - void addAfterPropsSetMethodIfNotExists(Element afterPropsSetMethod) { - String methodName = afterPropsSetMethod.getSimpleName().toString(); - if (!afterPropsSetMethodNames.contains(methodName)) { - afterPropsSetMethodNames.add(methodName); - } - } - - ResourceValue getLayoutResource(ResourceProcessor resourceProcessor) { - ModelView annotation = viewElement.getAnnotation(ModelView.class); - int layoutValue = annotation.defaultLayout(); - if (layoutValue != 0) { - return resourceProcessor.getLayoutInAnnotation(viewElement, ModelView.class); - } - - PackageModelViewSettings modelViewConfig = - configManager.getModelViewConfig(viewElement); - - if (modelViewConfig != null) { - return modelViewConfig.getNameForView(viewElement); - } - - errorLogger.logError("Unable to get layout resource for view %s", viewElement.getSimpleName()); - return new ResourceValue(0); - } - - List getResetMethodNames() { - return resetMethodNames; - } - - List getAfterPropsSetMethodNames() { - return afterPropsSetMethodNames; - } - - List getViewAttributes() { - List result = new ArrayList<>(attributeInfo.size()); - for (AttributeInfo info : attributeInfo) { - if (info instanceof ViewAttributeInfo) { - result.add((ViewAttributeInfo) info); - } - } - - return result; - } -} diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.kt b/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.kt new file mode 100644 index 0000000000..c49b657727 --- /dev/null +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewInfo.kt @@ -0,0 +1,169 @@ +package com.airbnb.epoxy + +import com.airbnb.epoxy.Utils.* +import com.squareup.javapoet.* +import javax.lang.model.element.* +import javax.lang.model.type.* +import javax.lang.model.util.* + +internal class ModelViewInfo( + val viewElement: TypeElement, + val typeUtils: Types, + val elements: Elements, + val errorLogger: ErrorLogger, + val configManager: ConfigManager, + private val resourceProcessor: ResourceProcessor +) : GeneratedModelInfo() { + val resetMethodNames = mutableListOf() + val afterPropsSetMethodNames = mutableListOf() + val saveViewState: Boolean + val viewAnnotation: ModelView = viewElement.getAnnotation(ModelView::class.java) + val fullSpanSize: Boolean + + val viewAttributes: List + get() = attributeInfo.filterIsInstance() + + init { + superClassElement = lookUpSuperClassElement() + this.superClassName = ParameterizedTypeName + .get(ClassName.get(superClassElement), TypeName.get(viewElement.asType())) + + generatedClassName = buildGeneratedModelName(viewElement, elements) + // We don't have any type parameters on our generated model + this.parametrizedClassName = generatedClassName + shouldGenerateModel = Modifier.ABSTRACT !in viewElement.modifiers + + if (superClassElement.simpleName.toString() != ClassNames.EPOXY_MODEL_UNTYPED.simpleName()) { + // If the view has a custom base model then we copy any custom constructors on it + constructors.addAll(getClassConstructors(superClassElement)) + } + + collectMethodsReturningClassType(superClassElement, typeUtils) + + // The bound type is the type of this view + boundObjectTypeName = ClassName.get(viewElement.asType()) + + saveViewState = viewAnnotation.saveViewState + layoutParams = viewAnnotation.autoLayout + fullSpanSize = viewAnnotation.fullSpan + includeOtherLayoutOptions = configManager.includeAlternateLayoutsForViews(viewElement) + } + + private fun lookUpSuperClassElement(): TypeElement { + val defaultSuper = Utils.getElementByName(ClassNames.EPOXY_MODEL_UNTYPED, + elements, typeUtils) as TypeElement + + // Unfortunately we have to do this weird try/catch to get the class type + var classToExtend: TypeMirror? = null + try { + viewAnnotation.baseModelClass // this should throw + } catch (mte: MirroredTypeException) { + classToExtend = mte.typeMirror + } + + if (classToExtend == null || classToExtend.toString() == Void::class.java.canonicalName) { + + val defaultBaseModel = configManager.getDefaultBaseModel(viewElement) + if (defaultBaseModel != null) { + classToExtend = defaultBaseModel + } else { + return defaultSuper + } + } + + if (!isEpoxyModel(classToExtend)) { + errorLogger + .logError( + "The base model provided to an %s must extend EpoxyModel, but was %s (%s).", + ModelView::class.java.simpleName, classToExtend, viewElement.simpleName) + return defaultSuper + } + + if (!validateSuperClassIsTypedCorrectly(classToExtend)) { + errorLogger.logError( + "The base model provided to an %s must have View as its type (%s).", + ModelView::class.java.simpleName, viewElement.simpleName) + return defaultSuper + } + + return typeUtils.asElement(classToExtend) as TypeElement + } + + /** The super class that our generated model extends from must have View as its only type. */ + private fun validateSuperClassIsTypedCorrectly(classType: TypeMirror): Boolean { + val classElement = typeUtils.asElement(classType) as? Parameterizable ?: return false + + val typeParameters = classElement.typeParameters + if (typeParameters.size != 1) { + // TODO: (eli_hart 6/15/17) It should be valid to have multiple or no types as long as they + // are correct, but that should be a rare case + return false + } + + val typeParam = typeParameters[0] + val bounds = typeParam.bounds + if (bounds.isEmpty()) { + // Any type is allowed, so View wil work + return true + } + + val typeMirror = bounds[0] + val viewType = Utils.getTypeMirror(ClassNames.ANDROID_VIEW, elements, typeUtils) + return typeUtils.isAssignable(viewType, typeMirror) || typeUtils.isSubtype(typeMirror, + viewType) + } + + private fun buildGeneratedModelName( + viewElement: TypeElement, + elementUtils: Elements + ): ClassName { + val packageName = elementUtils.getPackageOf(viewElement).qualifiedName.toString() + + var className = viewElement.simpleName.toString() + className += GeneratedModelInfo.GENERATED_MODEL_SUFFIX + + return ClassName.get(packageName, className) + } + + fun addProp(propMethod: ExecutableElement) { + addAttribute(ViewAttributeInfo(this, propMethod, typeUtils, elements, errorLogger, + resourceProcessor)) + } + + fun addPropIfNotExists(propMethod: ExecutableElement) { + addAttributeIfNotExists( + ViewAttributeInfo(this, propMethod, typeUtils, elements, errorLogger, + resourceProcessor)) + } + + fun addOnRecycleMethodIfNotExists(resetMethod: Element) { + val methodName = resetMethod.simpleName.toString() + if (!resetMethodNames.contains(methodName)) { + resetMethodNames.add(methodName) + } + } + + fun addAfterPropsSetMethodIfNotExists(afterPropsSetMethod: Element) { + val methodName = afterPropsSetMethod.simpleName.toString() + if (!afterPropsSetMethodNames.contains(methodName)) { + afterPropsSetMethodNames.add(methodName) + } + } + + fun getLayoutResource(resourceProcessor: ResourceProcessor): ResourceValue { + val annotation = viewElement.getAnnotation(ModelView::class.java) + val layoutValue = annotation.defaultLayout + if (layoutValue != 0) { + return resourceProcessor.getLayoutInAnnotation(viewElement, ModelView::class.java) + } + + val modelViewConfig = configManager.getModelViewConfig(viewElement) + + if (modelViewConfig != null) { + return modelViewConfig.getNameForView(viewElement) + } + + errorLogger.logError("Unable to get layout resource for view %s", viewElement.simpleName) + return ResourceValue(0) + } +} diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewProcessor.kt b/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewProcessor.kt index 9748455cee..98a6348cf4 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewProcessor.kt +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewProcessor.kt @@ -287,7 +287,7 @@ internal class ModelViewProcessor( // different annotation parameter settings, or we could end up with duplicates forEachElementWithAnnotation(modelPropAnnotations) { - view.addPropIfNotExists(it as ExecutableElement?) + view.addPropIfNotExists(it as ExecutableElement) } forEachElementWithAnnotation(listOf(OnViewRecycled::class.java)) { diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewWriter.kt b/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewWriter.kt index bc3c9f33ff..18f6518d73 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewWriter.kt +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelViewWriter.kt @@ -372,7 +372,7 @@ internal class ModelViewWriter( modelViewInfo: ModelViewInfo, unbindParamName: String ) { - for (methodName in modelViewInfo.getResetMethodNames()) { + for (methodName in modelViewInfo.resetMethodNames) { builder.addStatement("$unbindParamName.$methodName()") } } @@ -382,7 +382,7 @@ internal class ModelViewWriter( modelInfo: ModelViewInfo, boundObjectParam: ParameterSpec ) { - for (methodName in modelInfo.getAfterPropsSetMethodNames()) { + for (methodName in modelInfo.afterPropsSetMethodNames) { methodBuilder.addStatement(boundObjectParam.name + "." + methodName + "()") } } diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/ViewAttributeInfo.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/ViewAttributeInfo.java index d7ffb6e0e2..06fd292b0e 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/ViewAttributeInfo.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/ViewAttributeInfo.java @@ -104,7 +104,7 @@ class ViewAttributeInfo extends AttributeInfo { // something and doesn't have its own javadoc createJavaDoc(elements.getDocComment(setterMethod), codeToSetDefault, constantFieldNameForDefaultValue, - modelInfo.viewElement, typeMirror, viewSetterMethodName); + modelInfo.getViewElement(), typeMirror, viewSetterMethodName); validatePropOptions(errorLogger, options, types, elements); @@ -184,7 +184,7 @@ private void assignDefaultValue(String defaultConstant, return; } - TypeElement viewClass = modelInfo.viewElement; + TypeElement viewClass = modelInfo.getViewElement(); while (viewClass != null) { for (Element element : viewClass.getEnclosedElements()) { if (checkElementForConstant(element, defaultConstant, types, errorLogger)) { @@ -192,13 +192,13 @@ private void assignDefaultValue(String defaultConstant, } } - viewClass = getParentClassElement(modelInfo.viewElement, types); + viewClass = getParentClassElement(modelInfo.getViewElement(), types); } errorLogger.logError( "The default value for (%s#%s) could not be found. Expected a constant named '%s' in the " + "view class.", - modelInfo.viewElement.getSimpleName(), viewSetterMethodName, defaultConstant); + modelInfo.getViewElement().getSimpleName(), viewSetterMethodName, defaultConstant); } private boolean checkElementForConstant(Element element, String constantName, Types types, @@ -213,7 +213,7 @@ private boolean checkElementForConstant(Element element, String constantName, Ty errorLogger.logError( "Default values for view props must be static, final, and not private. (%s#%s)", - modelInfo.viewElement.getSimpleName(), viewSetterMethodName); + modelInfo.getViewElement().getSimpleName(), viewSetterMethodName); return true; } @@ -221,13 +221,13 @@ private boolean checkElementForConstant(Element element, String constantName, Ty if (!types.isAssignable(element.asType(), typeMirror)) { errorLogger.logError( "The default value for (%s#%s) must be a %s.", - modelInfo.viewElement.getSimpleName(), viewSetterMethodName, typeMirror); + modelInfo.getViewElement().getSimpleName(), viewSetterMethodName, typeMirror); return true; } constantFieldNameForDefaultValue = constantName; codeToSetDefault.explicit = - CodeBlock.of("$T.$L", ClassName.get(modelInfo.viewElement), constantName); + CodeBlock.of("$T.$L", ClassName.get(modelInfo.getViewElement()), constantName); return true; } @@ -356,7 +356,7 @@ String generatedGetterName() { @Override public String toString() { return "View Prop {" - + "view='" + modelInfo.viewElement.getSimpleName() + '\'' + + "view='" + modelInfo.getViewElement().getSimpleName() + '\'' + ", name='" + viewSetterMethodName + '\'' + ", type=" + getTypeName() + '}'; diff --git a/epoxy-processortest/src/test/java/com/airbnb/epoxy/ViewProcessorTest.kt b/epoxy-processortest/src/test/java/com/airbnb/epoxy/ViewProcessorTest.kt index 62ad7b3ba5..5aa258f8ba 100644 --- a/epoxy-processortest/src/test/java/com/airbnb/epoxy/ViewProcessorTest.kt +++ b/epoxy-processortest/src/test/java/com/airbnb/epoxy/ViewProcessorTest.kt @@ -151,6 +151,7 @@ class ViewProcessorTest { + "import java.util.List;\n" + "\n" + "public abstract class TestBaseModel extends EpoxyModel {\n" + + " public TestBaseModel(long id) { super(id); }" + "\n" + " @Override\n" + " public void bind(T view) {\n" diff --git a/epoxy-processortest/src/test/resources/BaseModelFromPackageConfigViewModel_.java b/epoxy-processortest/src/test/resources/BaseModelFromPackageConfigViewModel_.java index 32b1339143..4894c2593e 100644 --- a/epoxy-processortest/src/test/resources/BaseModelFromPackageConfigViewModel_.java +++ b/epoxy-processortest/src/test/resources/BaseModelFromPackageConfigViewModel_.java @@ -24,6 +24,10 @@ public class BaseModelViewModel_ extends TestBaseModel implements * Bitset index: 0 */ private String clickListener_String; + public BaseModelViewModel_() { + super(); + } + @Override public void addTo(EpoxyController controller) { super.addTo(controller); diff --git a/epoxy-processortest/src/test/resources/BaseModelViewModel_.java b/epoxy-processortest/src/test/resources/BaseModelViewModel_.java index 32b1339143..e38f09d1f6 100644 --- a/epoxy-processortest/src/test/resources/BaseModelViewModel_.java +++ b/epoxy-processortest/src/test/resources/BaseModelViewModel_.java @@ -24,6 +24,10 @@ public class BaseModelViewModel_ extends TestBaseModel implements * Bitset index: 0 */ private String clickListener_String; + public BaseModelViewModel_(long id) { + super(id); + } + @Override public void addTo(EpoxyController controller) { super.addTo(controller); diff --git a/epoxy-processortest/src/test/resources/BaseModelViewWithSuperDiffBindModel_.java b/epoxy-processortest/src/test/resources/BaseModelViewWithSuperDiffBindModel_.java index c03db2644e..2e2547c3f2 100644 --- a/epoxy-processortest/src/test/resources/BaseModelViewWithSuperDiffBindModel_.java +++ b/epoxy-processortest/src/test/resources/BaseModelViewWithSuperDiffBindModel_.java @@ -24,6 +24,10 @@ public class BaseModelViewModel_ extends TestBaseModel implements * Bitset index: 0 */ private String clickListener_String; + public BaseModelViewModel_() { + super(); + } + @Override public void addTo(EpoxyController controller) { super.addTo(controller); diff --git a/epoxy-processortest/src/test/resources/BaseModelWithAttributeViewModel_.java b/epoxy-processortest/src/test/resources/BaseModelWithAttributeViewModel_.java index 486529e6ab..10af99a8a9 100644 --- a/epoxy-processortest/src/test/resources/BaseModelWithAttributeViewModel_.java +++ b/epoxy-processortest/src/test/resources/BaseModelWithAttributeViewModel_.java @@ -24,6 +24,10 @@ public class BaseModelViewModel_ extends TestBaseModel implements * Bitset index: 0 */ private String clickListener_String; + public BaseModelViewModel_() { + super(); + } + @Override public void addTo(EpoxyController controller) { super.addTo(controller); diff --git a/epoxy-processortest/src/test/resources/TestAfterBindPropsViewModel_.java b/epoxy-processortest/src/test/resources/TestAfterBindPropsViewModel_.java index eaa57d9931..c18747aca7 100644 --- a/epoxy-processortest/src/test/resources/TestAfterBindPropsViewModel_.java +++ b/epoxy-processortest/src/test/resources/TestAfterBindPropsViewModel_.java @@ -126,7 +126,7 @@ public boolean flag() { /** * Optional: Default value is false * - * @see TestAfterBindPropsSuperView#setFlagSuper(boolean) + * @see TestAfterBindPropsView#setFlagSuper(boolean) */ public TestAfterBindPropsViewModel_ flagSuper(boolean flagSuper) { assignedAttributes_epoxyGeneratedModel.set(1);