From d11379bd2aaf5188709fbfcda1330e3f0660daa1 Mon Sep 17 00:00:00 2001 From: Ilya Zorin Date: Mon, 6 Mar 2017 21:04:49 +0200 Subject: [PATCH] Allow to annotate private fields based on their getters and setters (#144) --- .../java/com/airbnb/epoxy/AttributeInfo.java | 65 ++- .../airbnb/epoxy/GeneratedModelWriter.java | 133 +++-- .../java/com/airbnb/epoxy/ModelProcessor.java | 2 +- .../java/com/airbnb/epoxy/ProcessorUtils.java | 16 +- .../com/airbnb/epoxy/ModelProcessorTest.java | 190 ++++++- .../resources/ModelWithAllFieldTypes_.java | 6 +- .../ModelWithAllPrivateFieldTypes.java | 200 ++++++++ .../ModelWithAllPrivateFieldTypes_.java | 480 ++++++++++++++++++ ...lWithPrivateFieldWithGetterWithParams.java | 18 + ...ateFieldWithGetterWithWrongReturnType.java | 18 + ...delWithPrivateFieldWithIsPrefixGetter.java | 18 + ...odelWithPrivateFieldWithPrivateGetter.java | 18 + ...odelWithPrivateFieldWithPrivateSetter.java | 18 + ...vateFieldWithSetterWithWrongParamType.java | 17 + ...hPrivateFieldWithSettterWithoutParams.java | 18 + ...ModelWithPrivateFieldWithStaticGetter.java | 18 + ...ModelWithPrivateFieldWithStaticSetter.java | 18 + .../ModelWithPrivateFieldWithoutGetter.java | 14 + ...thPrivateFieldWithoutGetterAndSetter.java} | 2 +- .../ModelWithPrivateFieldWithoutSetter.java | 14 + .../ModelWithPrivateViewClickListener.java | 25 + .../ModelWithPrivateViewClickListener_.java | 196 +++++++ 22 files changed, 1442 insertions(+), 62 deletions(-) create mode 100755 epoxy-processortest/src/test/resources/ModelWithAllPrivateFieldTypes.java create mode 100755 epoxy-processortest/src/test/resources/ModelWithAllPrivateFieldTypes_.java create mode 100755 epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithGetterWithParams.java create mode 100755 epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithGetterWithWrongReturnType.java create mode 100755 epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithIsPrefixGetter.java create mode 100755 epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithPrivateGetter.java create mode 100755 epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithPrivateSetter.java create mode 100755 epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithSetterWithWrongParamType.java create mode 100755 epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithSettterWithoutParams.java create mode 100755 epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithStaticGetter.java create mode 100755 epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithStaticSetter.java create mode 100755 epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithoutGetter.java rename epoxy-processortest/src/test/resources/{ModelWithPrivateField.java => ModelWithPrivateFieldWithoutGetterAndSetter.java} (61%) create mode 100755 epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithoutSetter.java create mode 100644 epoxy-processortest/src/test/resources/ModelWithPrivateViewClickListener.java create mode 100644 epoxy-processortest/src/test/resources/ModelWithPrivateViewClickListener_.java diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/AttributeInfo.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/AttributeInfo.java index a99ef7cffd..9e55d69643 100755 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/AttributeInfo.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/AttributeInfo.java @@ -16,16 +16,19 @@ 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.type.DeclaredType; import javax.lang.model.util.Types; +import static com.airbnb.epoxy.ProcessorUtils.capitalizeFirstLetter; import static com.airbnb.epoxy.ProcessorUtils.isViewClickListenerType; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PROTECTED; import static javax.lang.model.element.Modifier.PUBLIC; +import static javax.lang.model.element.Modifier.STATIC; class AttributeInfo { @@ -49,6 +52,11 @@ class AttributeInfo { private final TypeElement classElement; + // for private fields (Kotlin case) + private final boolean isPrivate; + private String getter; + private String setter; + AttributeInfo(Element attribute, Types typeUtils, ErrorLogger errorLogger) { attributeElement = attribute; this.typeUtils = typeUtils; @@ -69,6 +77,12 @@ class AttributeInfo { generateSetter = annotation.setter() && !options.contains(Option.NoSetter); generateGetter = !options.contains(Option.NoGetter); + + isPrivate = attribute.getModifiers().contains(PRIVATE); + if (isPrivate) { + findGetterAndSetterForPrivateField(errorLogger); + } + buildAnnotationLists(attribute.getAnnotationMirrors()); } @@ -141,6 +155,43 @@ private boolean isFieldPackagePrivate(Element attribute) { && !modifiers.contains(PRIVATE); } + /** + * Checks if the given private field has getter and setter for access to it + */ + private void findGetterAndSetterForPrivateField(ErrorLogger errorLogger) { + for (Element element : classElement.getEnclosedElements()) { + if (element.getKind() == ElementKind.METHOD) { + ExecutableElement method = (ExecutableElement) element; + String methodName = method.getSimpleName().toString(); + // check if it is a valid getter + if ((methodName.equals(String.format("get%s", capitalizeFirstLetter(name))) + || methodName.equals(String.format("is%s", capitalizeFirstLetter(name)))) + && !method.getModifiers().contains(PRIVATE) + && !method.getModifiers().contains(STATIC) + && method.getParameters().isEmpty() + && TypeName.get(method.getReturnType()).equals(type)) { + getter = methodName; + } + // check if it is a valid setter + if ((methodName.equals(String.format("set%s", capitalizeFirstLetter(name)))) + && !method.getModifiers().contains(PRIVATE) + && !method.getModifiers().contains(STATIC) + && method.getParameters().size() == 1 + && TypeName.get(method.getParameters().get(0).asType()).equals(type)) { + setter = methodName; + } + } + } + if (getter == null || setter == null) { + errorLogger + .logError("%s annotations must not be on private fields" + + " without proper getter and setter methods. (class: %s, field: %s)", + EpoxyAttribute.class, + classElement.getSimpleName(), + name); + } + } + /** * Keeps track of annotations on the attribute so that they can be used in the generated setter * and getter method. Setter and getter annotations are stored separately since the annotation may @@ -204,7 +255,7 @@ List getSetterAnnotations() { return setterAnnotations; } - public boolean generateGetter() { + boolean generateGetter() { return generateGetter; } @@ -224,6 +275,18 @@ boolean isPackagePrivate() { return packagePrivate; } + boolean isPrivate() { + return isPrivate; + } + + String getter() { + return getter; + } + + String setter() { + return setter; + } + @Override public String toString() { return "ModelAttributeData{" diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java index 1e8aa1ce4e..bde820ecc1 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java @@ -46,7 +46,6 @@ import static com.squareup.javapoet.TypeName.INT; import static com.squareup.javapoet.TypeName.LONG; import static com.squareup.javapoet.TypeName.SHORT; -import static java.awt.SystemColor.info; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; @@ -215,29 +214,35 @@ private Iterable generateBindMethods(ClassToGenerateInfo classInfo) // view click listener of the original model. String modelClickListenerField = attribute.getModelClickListenerName(); - preBindBuilder - .beginControlFlow("if ($L != null)", modelClickListenerField) - .addCode(CodeBlock.of( - "super.$L = new $T() {\n" - + " // Save the original click listener so if it gets changed on\n" - + " // the generated model this click listener won't be affected\n" - + " // if it is still bound to a view.\n" - + " private final $T $L = $T.this.$L;\n" - + " public void onClick($T v) {\n" - + " $L.onClick($T.this, object, v,\n" - + " holder.getAdapterPosition());\n" - + " }\n" - + " public int hashCode() {\n" - + " // Use the hash of the original click listener so we don't change the\n" - + " // value by wrapping it with this anonymous click listener\n" - + " return $L.hashCode();\n" - + " }\n" - + " };\n", attribute.getName(), viewClickListenerType, - getModelClickListenerType(classInfo), - modelClickListenerField, classInfo.getGeneratedName(), - modelClickListenerField, viewType, modelClickListenerField, - classInfo.getGeneratedName(), modelClickListenerField)) - .endControlFlow(); + preBindBuilder.beginControlFlow("if ($L != null)", modelClickListenerField); + CodeBlock clickListenerCodeBlock = CodeBlock.of( + "{\n" + + " // Save the original click listener so if it gets changed on\n" + + " // the generated model this click listener won't be affected\n" + + " // if it is still bound to a view.\n" + + " private final $T $L = $T.this.$L;\n" + + " public void onClick($T v) {\n" + + " $L.onClick($T.this, object, v,\n" + + " holder.getAdapterPosition());\n" + + " }\n" + + " public int hashCode() {\n" + + " // Use the hash of the original click listener so we don't change the\n" + + " // value by wrapping it with this anonymous click listener\n" + + " return $L.hashCode();\n" + + " }\n" + + " }", + getModelClickListenerType(classInfo), + modelClickListenerField, classInfo.getGeneratedName(), + modelClickListenerField, viewType, modelClickListenerField, + classInfo.getGeneratedName(), modelClickListenerField); + if (attribute.isPrivate()) { + preBindBuilder.addCode("super.$L(new $T() $L);\n", attribute.setter(), + viewClickListenerType, clickListenerCodeBlock); + } else { + preBindBuilder.addCode("super.$L = new $T() $L;\n", attribute.getName(), + viewClickListenerType, clickListenerCodeBlock); + } + preBindBuilder.endControlFlow(); } methods.add(preBindBuilder.build()); @@ -527,9 +532,15 @@ private MethodSpec generateSetClickModelListener(ClassToGenerateInfo helperClass .addModifiers(PUBLIC) .returns(helperClass.getParameterizedGeneratedName()) .addParameter(param) - .addAnnotations(attribute.getSetterAnnotations()) - .addStatement("super.$L = null", attributeName) - .addStatement("this.$L = $L", attribute.getModelClickListenerName(), attributeName); + .addAnnotations(attribute.getSetterAnnotations()); + + if (attribute.isPrivate()) { + builder.addStatement("super.$L(null)", attribute.setter()); + } else { + builder.addStatement("super.$L = null", attributeName); + } + + builder.addStatement("this.$L = $L", attribute.getModelClickListenerName(), attributeName); return builder .addStatement("return this") @@ -575,7 +586,13 @@ private MethodSpec generateEquals(ClassToGenerateInfo helperClass) { continue; } - addEqualsLineForType(builder, attributeInfo.useInHash(), type, attributeInfo.getName()); + if (attributeInfo.isPrivate()) { + addEqualsLineForType(builder, attributeInfo.useInHash(), type, + String.format("%s()", attributeInfo.getter())); + } else { + addEqualsLineForType(builder, attributeInfo.useInHash(), type, attributeInfo.getName()); + } + if (attributeInfo.isViewClickListener()) { // Add the model click listener as well addEqualsLineForType(builder, attributeInfo.useInHash(), type, @@ -661,7 +678,12 @@ private MethodSpec generateHashCode(ClassToGenerateInfo helperClass) { continue; } - addHashCodeLineForType(builder, attributeInfo.useInHash(), type, attributeInfo.getName()); + if (attributeInfo.isPrivate()) { + addHashCodeLineForType(builder, attributeInfo.useInHash(), type, + String.format("%s()", attributeInfo.getter())); + } else { + addHashCodeLineForType(builder, attributeInfo.useInHash(), type, attributeInfo.getName()); + } if (attributeInfo.isViewClickListener()) { // Add the model click listener as well @@ -714,10 +736,18 @@ private MethodSpec generateToString(ClassToGenerateInfo helperClass) { for (AttributeInfo attributeInfo : helperClass.getAttributeInfo()) { String attributeName = attributeInfo.getName(); if (first) { - sb.append(String.format("\"%s=\" + %s +\n", attributeName, attributeName)); + if (attributeInfo.isPrivate()) { + sb.append(String.format("\"%s=\" + %s() +\n", attributeName, attributeInfo.getter())); + } else { + sb.append(String.format("\"%s=\" + %s +\n", attributeName, attributeName)); + } first = false; } else { - sb.append(String.format("\", %s=\" + %s +\n", attributeName, attributeName)); + if (attributeInfo.isPrivate()) { + sb.append(String.format("\", %s=\" + %s() +\n", attributeName, attributeInfo.getter())); + } else { + sb.append(String.format("\", %s=\" + %s +\n", attributeName, attributeName)); + } } } @@ -729,12 +759,18 @@ private MethodSpec generateToString(ClassToGenerateInfo helperClass) { } private MethodSpec generateGetter(AttributeInfo data) { - return MethodSpec.methodBuilder(data.getName()) + MethodSpec.Builder builder = MethodSpec.methodBuilder(data.getName()) .addModifiers(PUBLIC) .returns(data.getType()) - .addAnnotations(data.getGetterAnnotations()) - .addStatement("return $L", data.getName()) - .build(); + .addAnnotations(data.getGetterAnnotations()); + + if (data.isPrivate()) { + builder.addStatement("return $L()", data.getter()); + } else { + builder.addStatement("return $L", data.getName()); + } + + return builder.build(); } private MethodSpec generateSetter(ClassToGenerateInfo helperClass, AttributeInfo attribute) { @@ -743,8 +779,13 @@ private MethodSpec generateSetter(ClassToGenerateInfo helperClass, AttributeInfo .addModifiers(PUBLIC) .returns(helperClass.getParameterizedGeneratedName()) .addParameter(ParameterSpec.builder(attribute.getType(), attributeName) - .addAnnotations(attribute.getSetterAnnotations()).build()) - .addStatement("this.$L = $L", attributeName, attributeName); + .addAnnotations(attribute.getSetterAnnotations()).build()); + + if (attribute.isPrivate()) { + builder.addStatement("this.$L($L)", attribute.setter(), attributeName); + } else { + builder.addStatement("this.$L = $L", attributeName, attributeName); + } if (attribute.isViewClickListener()) { // Null out the model click listener since this view click listener should replace it @@ -770,8 +811,13 @@ private MethodSpec generateReset(ClassToGenerateInfo helperClass) { for (AttributeInfo attributeInfo : helperClass.getAttributeInfo()) { if (!attributeInfo.hasFinalModifier()) { - builder.addStatement("this.$L = $L", attributeInfo.getName(), - getDefaultValue(attributeInfo.getType())); + if (attributeInfo.isPrivate()) { + builder.addStatement("this.$L($L)", attributeInfo.setter(), + getDefaultValue(attributeInfo.getType())); + } else { + builder.addStatement("this.$L = $L", attributeInfo.getName(), + getDefaultValue(attributeInfo.getType())); + } } if (attributeInfo.isViewClickListener()) { @@ -788,9 +834,14 @@ private MethodSpec generateReset(ClassToGenerateInfo helperClass) { private static String getDefaultValue(TypeName attributeType) { if (attributeType == BOOLEAN) { return "false"; - } else if (attributeType == BYTE || attributeType == CHAR || attributeType == SHORT - || attributeType == INT) { + } else if (attributeType == INT) { return "0"; + } else if (attributeType == BYTE) { + return "(byte) 0"; + } else if (attributeType == CHAR) { + return "(char) 0"; + } else if (attributeType == SHORT) { + return "(short) 0"; } else if (attributeType == LONG) { return "0L"; } else if (attributeType == FLOAT) { diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelProcessor.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelProcessor.java index d95e984e0d..c3937152e0 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelProcessor.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/ModelProcessor.java @@ -124,7 +124,7 @@ private void addAttributeToGeneratedClass(Element attribute, } private AttributeInfo buildAttributeInfo(Element attribute) { - validateFieldAccessibleViaGeneratedCode(attribute, EpoxyAttribute.class, errorLogger); + validateFieldAccessibleViaGeneratedCode(attribute, EpoxyAttribute.class, errorLogger, true); return new AttributeInfo(attribute, typeUtils, errorLogger); } diff --git a/epoxy-processor/src/main/java/com/airbnb/epoxy/ProcessorUtils.java b/epoxy-processor/src/main/java/com/airbnb/epoxy/ProcessorUtils.java index 80f8690476..cf6484a263 100644 --- a/epoxy-processor/src/main/java/com/airbnb/epoxy/ProcessorUtils.java +++ b/epoxy-processor/src/main/java/com/airbnb/epoxy/ProcessorUtils.java @@ -210,12 +210,12 @@ static TypeMirror getEpoxyObjectType(TypeElement clazz, Types typeUtils) { } static void validateFieldAccessibleViaGeneratedCode(Element fieldElement, - Class annotationClass, ErrorLogger errorLogger) { + Class annotationClass, ErrorLogger errorLogger, boolean skipPrivateFieldCheck) { TypeElement enclosingElement = (TypeElement) fieldElement.getEnclosingElement(); // Verify method modifiers. Set modifiers = fieldElement.getModifiers(); - if (modifiers.contains(PRIVATE) || modifiers.contains(STATIC)) { + if ((modifiers.contains(PRIVATE) && !skipPrivateFieldCheck) || modifiers.contains(STATIC)) { errorLogger.logError( "%s annotations must not be on private or static fields. (class: %s, field: %s)", annotationClass.getSimpleName(), @@ -248,4 +248,16 @@ static void validateFieldAccessibleViaGeneratedCode(Element fieldElement, enclosingElement.getSimpleName(), fieldElement.getSimpleName()); } } + + static void validateFieldAccessibleViaGeneratedCode(Element fieldElement, + Class annotationClass, ErrorLogger errorLogger) { + validateFieldAccessibleViaGeneratedCode(fieldElement, annotationClass, errorLogger, false); + } + + static String capitalizeFirstLetter(String original) { + if (original == null || original.isEmpty()) { + return original; + } + return original.substring(0, 1).toUpperCase() + original.substring(1); + } } diff --git a/epoxy-processortest/src/test/java/com/airbnb/epoxy/ModelProcessorTest.java b/epoxy-processortest/src/test/java/com/airbnb/epoxy/ModelProcessorTest.java index a3ad0c6d01..9892de5b17 100755 --- a/epoxy-processortest/src/test/java/com/airbnb/epoxy/ModelProcessorTest.java +++ b/epoxy-processortest/src/test/java/com/airbnb/epoxy/ModelProcessorTest.java @@ -15,6 +15,7 @@ * android EpoxyAdapter library that contains the EpoxyModel. */ +@SuppressWarnings("ResultOfMethodCallIgnored") public class ModelProcessorTest { @Test public void testSimpleModel() { @@ -168,18 +169,6 @@ public void testModelWithFinalAttribute() { .generatesSources(generatedModel); } - @Test - public void testModelWithPrivateAttributeFails() { - JavaFileObject model = JavaFileObjects - .forResource("ModelWithPrivateField.java"); - - assert_().about(javaSource()) - .that(model) - .processedWith(new EpoxyProcessor()) - .failsToCompile() - .withErrorContaining("private"); - } - @Test public void testModelWithStaticAttributeFails() { JavaFileObject model = JavaFileObjects @@ -282,7 +271,7 @@ public void testModelWithAbstractClass() { // We don't generate subclasses if the model is abstract unless it has a class annotation. boolean modelNotGenerated; try { - JavaFileObject generatedModel = JavaFileObjects.forResource("ModelWithAbstractClass_.java"); + JavaFileObjects.forResource("ModelWithAbstractClass_.java"); modelNotGenerated = false; } catch (IllegalArgumentException e) { modelNotGenerated = true; @@ -498,4 +487,179 @@ public void modelWithViewClickListener() { .and() .generatesSources(generatedNoLayoutModel); } + + @Test + public void testModelWithPrivateAttributeWithoutGetterAndSetterFails() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithoutGetterAndSetter.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .failsToCompile() + .withErrorContaining("private fields without proper getter and setter"); + } + + @Test + public void testModelWithPrivateAttributeWithoutSetterFails() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithoutSetter.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .failsToCompile() + .withErrorContaining("private fields without proper getter and setter"); + } + + @Test + public void testModelWithPrivateAttributeWithoutGetterFails() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithoutGetter.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .failsToCompile() + .withErrorContaining("private fields without proper getter and setter"); + } + + @Test + public void testModelWithPrivateAttributeWithIsPrefixGetter() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithIsPrefixGetter.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .compilesWithoutError(); + } + + @Test + public void testModelWithPrivateAttributeWithPrivateGetterFails() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithPrivateGetter.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .failsToCompile() + .withErrorContaining("private fields without proper getter and setter"); + } + + @Test + public void testModelWithPrivateAttributeWithStaticGetterFails() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithStaticGetter.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .failsToCompile() + .withErrorContaining("private fields without proper getter and setter"); + } + + @Test + public void testModelWithPrivateAttributeWithGetterWithParamsFails() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithGetterWithParams.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .failsToCompile() + .withErrorContaining("private fields without proper getter and setter"); + } + + @Test + public void testModelWithPrivateAttributeWithGetterWithWrongReturnTypeFails() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithGetterWithWrongReturnType.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .failsToCompile() + .withErrorContaining("private fields without proper getter and setter"); + } + + @Test + public void testModelWithPrivateAttributeWithPrivateSetterFails() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithPrivateSetter.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .failsToCompile() + .withErrorContaining("private fields without proper getter and setter"); + } + + @Test + public void testModelWithPrivateAttributeWithStaticSetterFails() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithStaticSetter.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .failsToCompile() + .withErrorContaining("private fields without proper getter and setter"); + } + + @Test + public void testModelWithPrivateAttributeWithGetterWithoutParamsFails() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithSettterWithoutParams.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .failsToCompile() + .withErrorContaining("private fields without proper getter and setter"); + } + + @Test + public void testModelWithPrivateAttributeWithSetterWithWrongReturnTypeFails() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateFieldWithSetterWithWrongParamType.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .failsToCompile() + .withErrorContaining("private fields without proper getter and setter"); + } + + @Test + public void testModelWithAllPrivateFieldTypes() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithAllPrivateFieldTypes.java"); + + JavaFileObject generatedModel = JavaFileObjects + .forResource("ModelWithAllPrivateFieldTypes_.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .compilesWithoutError() + .and() + .generatesSources(generatedModel); + } + + @Test + public void modelWithViewPrivateClickListener() { + JavaFileObject model = JavaFileObjects + .forResource("ModelWithPrivateViewClickListener.java"); + + JavaFileObject generatedNoLayoutModel = JavaFileObjects + .forResource("ModelWithPrivateViewClickListener_.java"); + + assert_().about(javaSource()) + .that(model) + .processedWith(new EpoxyProcessor()) + .compilesWithoutError() + .and() + .generatesSources(generatedNoLayoutModel); + } } diff --git a/epoxy-processortest/src/test/resources/ModelWithAllFieldTypes_.java b/epoxy-processortest/src/test/resources/ModelWithAllFieldTypes_.java index 25651aeccc..6651390c32 100755 --- a/epoxy-processortest/src/test/resources/ModelWithAllFieldTypes_.java +++ b/epoxy-processortest/src/test/resources/ModelWithAllFieldTypes_.java @@ -301,18 +301,18 @@ public ModelWithAllFieldTypes_ reset() { onModelUnboundListener_epoxyGeneratedModel = null; this.valueInteger = null; - this.valueShort = 0; + this.valueShort = (short) 0; this.valueLong = 0L; this.valueList = null; this.valueShortWrapper = null; this.valueDouble = 0.0d; - this.valueChar = 0; + this.valueChar = (char) 0; this.valueInt = 0; this.valueDoubleWrapper = null; this.valueFloatWrapper = null; this.valueBooleanWrapper = null; this.valueByteWrapper = null; - this.valuebByte = 0; + this.valuebByte = (byte) 0; this.valueLongWrapper = null; this.valueCharacter = null; this.valueString = null; diff --git a/epoxy-processortest/src/test/resources/ModelWithAllPrivateFieldTypes.java b/epoxy-processortest/src/test/resources/ModelWithAllPrivateFieldTypes.java new file mode 100755 index 0000000000..9aab780472 --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithAllPrivateFieldTypes.java @@ -0,0 +1,200 @@ +package com.airbnb.epoxy; + +import java.util.List; + +public class ModelWithAllPrivateFieldTypes extends EpoxyModel { + @EpoxyAttribute private int valueInt; + @EpoxyAttribute private Integer valueInteger; + @EpoxyAttribute private short valueShort; + @EpoxyAttribute private Short valueShortWrapper; + @EpoxyAttribute private char valueChar; + @EpoxyAttribute private Character valueCharacter; + @EpoxyAttribute private byte valuebByte; + @EpoxyAttribute private Byte valueByteWrapper; + @EpoxyAttribute private long valueLong; + @EpoxyAttribute private Long valueLongWrapper; + @EpoxyAttribute private double valueDouble; + @EpoxyAttribute private Double valueDoubleWrapper; + @EpoxyAttribute private float valueFloat; + @EpoxyAttribute private Float valueFloatWrapper; + @EpoxyAttribute private boolean valueBoolean; + @EpoxyAttribute private Boolean valueBooleanWrapper; + @EpoxyAttribute private int[] valueIntArray; + @EpoxyAttribute private Object[] valueObjectArray; + @EpoxyAttribute private String valueString; + @EpoxyAttribute private Object valueObject; + @EpoxyAttribute private List valueList; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public int getValueInt() { + return valueInt; + } + + public void setValueInt(int valueInt) { + this.valueInt = valueInt; + } + + public Integer getValueInteger() { + return valueInteger; + } + + public void setValueInteger(Integer valueInteger) { + this.valueInteger = valueInteger; + } + + public short getValueShort() { + return valueShort; + } + + public void setValueShort(short valueShort) { + this.valueShort = valueShort; + } + + public Short getValueShortWrapper() { + return valueShortWrapper; + } + + public void setValueShortWrapper(Short valueShortWrapper) { + this.valueShortWrapper = valueShortWrapper; + } + + public char getValueChar() { + return valueChar; + } + + public void setValueChar(char valueChar) { + this.valueChar = valueChar; + } + + public Character getValueCharacter() { + return valueCharacter; + } + + public void setValueCharacter(Character valueCharacter) { + this.valueCharacter = valueCharacter; + } + + public byte getValuebByte() { + return valuebByte; + } + + public void setValuebByte(byte valuebByte) { + this.valuebByte = valuebByte; + } + + public Byte getValueByteWrapper() { + return valueByteWrapper; + } + + public void setValueByteWrapper(Byte valueByteWrapper) { + this.valueByteWrapper = valueByteWrapper; + } + + public long getValueLong() { + return valueLong; + } + + public void setValueLong(long valueLong) { + this.valueLong = valueLong; + } + + public Long getValueLongWrapper() { + return valueLongWrapper; + } + + public void setValueLongWrapper(Long valueLongWrapper) { + this.valueLongWrapper = valueLongWrapper; + } + + public double getValueDouble() { + return valueDouble; + } + + public void setValueDouble(double valueDouble) { + this.valueDouble = valueDouble; + } + + public Double getValueDoubleWrapper() { + return valueDoubleWrapper; + } + + public void setValueDoubleWrapper(Double valueDoubleWrapper) { + this.valueDoubleWrapper = valueDoubleWrapper; + } + + public float getValueFloat() { + return valueFloat; + } + + public void setValueFloat(float valueFloat) { + this.valueFloat = valueFloat; + } + + public Float getValueFloatWrapper() { + return valueFloatWrapper; + } + + public void setValueFloatWrapper(Float valueFloatWrapper) { + this.valueFloatWrapper = valueFloatWrapper; + } + + public boolean isValueBoolean() { + return valueBoolean; + } + + public void setValueBoolean(boolean valueBoolean) { + this.valueBoolean = valueBoolean; + } + + public Boolean getValueBooleanWrapper() { + return valueBooleanWrapper; + } + + public void setValueBooleanWrapper(Boolean valueBooleanWrapper) { + this.valueBooleanWrapper = valueBooleanWrapper; + } + + public int[] getValueIntArray() { + return valueIntArray; + } + + public void setValueIntArray(int[] valueIntArray) { + this.valueIntArray = valueIntArray; + } + + public Object[] getValueObjectArray() { + return valueObjectArray; + } + + public void setValueObjectArray(Object[] valueObjectArray) { + this.valueObjectArray = valueObjectArray; + } + + public String getValueString() { + return valueString; + } + + public void setValueString(String valueString) { + this.valueString = valueString; + } + + public Object getValueObject() { + return valueObject; + } + + public void setValueObject(Object valueObject) { + this.valueObject = valueObject; + } + + public List getValueList() { + return valueList; + } + + public void setValueList(List valueList) { + this.valueList = valueList; + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithAllPrivateFieldTypes_.java b/epoxy-processortest/src/test/resources/ModelWithAllPrivateFieldTypes_.java new file mode 100755 index 0000000000..d257360103 --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithAllPrivateFieldTypes_.java @@ -0,0 +1,480 @@ +package com.airbnb.epoxy; + +import android.support.annotation.LayoutRes; +import java.lang.Boolean; +import java.lang.Byte; +import java.lang.CharSequence; +import java.lang.Character; +import java.lang.Double; +import java.lang.Float; +import java.lang.Integer; +import java.lang.Long; +import java.lang.Object; +import java.lang.Override; +import java.lang.Short; +import java.lang.String; +import java.util.Arrays; +import java.util.List; + +/** + * Generated file. Do not modify! */ +public class ModelWithAllPrivateFieldTypes_ extends ModelWithAllPrivateFieldTypes implements GeneratedModel { + private OnModelBoundListener onModelBoundListener_epoxyGeneratedModel; + + private OnModelUnboundListener onModelUnboundListener_epoxyGeneratedModel; + + public ModelWithAllPrivateFieldTypes_() { + super(); + } + + @Override + public void handlePreBind(final EpoxyViewHolder holder, final Object object) { + } + + @Override + public void handlePostBind(final EpoxyViewHolder holder, final Object object) { + if (onModelBoundListener_epoxyGeneratedModel != null) { + onModelBoundListener_epoxyGeneratedModel.onModelBound(this, object); + } + } + + /** + * Register a listener that will be called when this model is bound to a view. + *

+ * The listener will contribute to this model's hashCode state per the {@link + * com.airbnb.epoxy.EpoxyAttribute.Option#DoNotHash} rules. + *

+ * You may clear the listener by setting a null value, or by calling {@link #reset()} */ + public ModelWithAllPrivateFieldTypes_ onBind(OnModelBoundListener listener) { + this.onModelBoundListener_epoxyGeneratedModel = listener; + return this; + } + + @Override + public void unbind(Object object) { + super.unbind(object); + if (onModelUnboundListener_epoxyGeneratedModel != null) { + onModelUnboundListener_epoxyGeneratedModel.onModelUnbound(this, object); + } + } + + /** + * Register a listener that will be called when this model is unbound from a view. + *

+ * The listener will contribute to this model's hashCode state per the {@link + * com.airbnb.epoxy.EpoxyAttribute.Option#DoNotHash} rules. + *

+ * You may clear the listener by setting a null value, or by calling {@link #reset()} */ + public ModelWithAllPrivateFieldTypes_ onUnbind(OnModelUnboundListener listener) { + this.onModelUnboundListener_epoxyGeneratedModel = listener; + return this; + } + + public ModelWithAllPrivateFieldTypes_ valueInteger(Integer valueInteger) { + this.setValueInteger(valueInteger); + return this; + } + + public Integer valueInteger() { + return getValueInteger(); + } + + public ModelWithAllPrivateFieldTypes_ valueShort(short valueShort) { + this.setValueShort(valueShort); + return this; + } + + public short valueShort() { + return getValueShort(); + } + + public ModelWithAllPrivateFieldTypes_ valueLong(long valueLong) { + this.setValueLong(valueLong); + return this; + } + + public long valueLong() { + return getValueLong(); + } + + public ModelWithAllPrivateFieldTypes_ valueList(List valueList) { + this.setValueList(valueList); + return this; + } + + public List valueList() { + return getValueList(); + } + + public ModelWithAllPrivateFieldTypes_ valueShortWrapper(Short valueShortWrapper) { + this.setValueShortWrapper(valueShortWrapper); + return this; + } + + public Short valueShortWrapper() { + return getValueShortWrapper(); + } + + public ModelWithAllPrivateFieldTypes_ valueDouble(double valueDouble) { + this.setValueDouble(valueDouble); + return this; + } + + public double valueDouble() { + return getValueDouble(); + } + + public ModelWithAllPrivateFieldTypes_ valueChar(char valueChar) { + this.setValueChar(valueChar); + return this; + } + + public char valueChar() { + return getValueChar(); + } + + public ModelWithAllPrivateFieldTypes_ valueInt(int valueInt) { + this.setValueInt(valueInt); + return this; + } + + public int valueInt() { + return getValueInt(); + } + + public ModelWithAllPrivateFieldTypes_ valueDoubleWrapper(Double valueDoubleWrapper) { + this.setValueDoubleWrapper(valueDoubleWrapper); + return this; + } + + public Double valueDoubleWrapper() { + return getValueDoubleWrapper(); + } + + public ModelWithAllPrivateFieldTypes_ valueFloatWrapper(Float valueFloatWrapper) { + this.setValueFloatWrapper(valueFloatWrapper); + return this; + } + + public Float valueFloatWrapper() { + return getValueFloatWrapper(); + } + + public ModelWithAllPrivateFieldTypes_ valueBooleanWrapper(Boolean valueBooleanWrapper) { + this.setValueBooleanWrapper(valueBooleanWrapper); + return this; + } + + public Boolean valueBooleanWrapper() { + return getValueBooleanWrapper(); + } + + public ModelWithAllPrivateFieldTypes_ valueByteWrapper(Byte valueByteWrapper) { + this.setValueByteWrapper(valueByteWrapper); + return this; + } + + public Byte valueByteWrapper() { + return getValueByteWrapper(); + } + + public ModelWithAllPrivateFieldTypes_ valuebByte(byte valuebByte) { + this.setValuebByte(valuebByte); + return this; + } + + public byte valuebByte() { + return getValuebByte(); + } + + public ModelWithAllPrivateFieldTypes_ valueLongWrapper(Long valueLongWrapper) { + this.setValueLongWrapper(valueLongWrapper); + return this; + } + + public Long valueLongWrapper() { + return getValueLongWrapper(); + } + + public ModelWithAllPrivateFieldTypes_ valueCharacter(Character valueCharacter) { + this.setValueCharacter(valueCharacter); + return this; + } + + public Character valueCharacter() { + return getValueCharacter(); + } + + public ModelWithAllPrivateFieldTypes_ valueString(String valueString) { + this.setValueString(valueString); + return this; + } + + public String valueString() { + return getValueString(); + } + + public ModelWithAllPrivateFieldTypes_ valueFloat(float valueFloat) { + this.setValueFloat(valueFloat); + return this; + } + + public float valueFloat() { + return getValueFloat(); + } + + public ModelWithAllPrivateFieldTypes_ valueBoolean(boolean valueBoolean) { + this.setValueBoolean(valueBoolean); + return this; + } + + public boolean valueBoolean() { + return isValueBoolean(); + } + + public ModelWithAllPrivateFieldTypes_ valueObjectArray(Object[] valueObjectArray) { + this.setValueObjectArray(valueObjectArray); + return this; + } + + public Object[] valueObjectArray() { + return getValueObjectArray(); + } + + public ModelWithAllPrivateFieldTypes_ valueObject(Object valueObject) { + this.setValueObject(valueObject); + return this; + } + + public Object valueObject() { + return getValueObject(); + } + + public ModelWithAllPrivateFieldTypes_ valueIntArray(int[] valueIntArray) { + this.setValueIntArray(valueIntArray); + return this; + } + + public int[] valueIntArray() { + return getValueIntArray(); + } + + @Override + public ModelWithAllPrivateFieldTypes_ getValueObject() { + super.getValueObject(); + return this; + } + + @Override + public ModelWithAllPrivateFieldTypes_ id(long id) { + super.id(id); + return this; + } + + @Override + public ModelWithAllPrivateFieldTypes_ id(CharSequence key) { + super.id(key); + return this; + } + + @Override + public ModelWithAllPrivateFieldTypes_ id(CharSequence key, long id) { + super.id(key, id); + return this; + } + + @Override + public ModelWithAllPrivateFieldTypes_ layout(@LayoutRes int arg0) { + super.layout(arg0); + return this; + } + + @Override + public ModelWithAllPrivateFieldTypes_ show() { + super.show(); + return this; + } + + @Override + public ModelWithAllPrivateFieldTypes_ show(boolean show) { + super.show(show); + return this; + } + + @Override + public ModelWithAllPrivateFieldTypes_ hide() { + super.hide(); + return this; + } + + @Override + public ModelWithAllPrivateFieldTypes_ reset() { + onModelBoundListener_epoxyGeneratedModel = null; + onModelUnboundListener_epoxyGeneratedModel = null; + this.setValueInteger(null); + this.setValueShort((short) 0); + this.setValueLong(0L); + this.setValueList(null); + this.setValueShortWrapper(null); + this.setValueDouble(0.0d); + this.setValueChar((char) 0); + this.setValueInt(0); + this.setValueDoubleWrapper(null); + this.setValueFloatWrapper(null); + this.setValueBooleanWrapper(null); + this.setValueByteWrapper(null); + this.setValuebByte((byte) 0); + this.setValueLongWrapper(null); + this.setValueCharacter(null); + this.setValueString(null); + this.setValueFloat(0.0f); + this.setValueBoolean(false); + this.setValueObjectArray(null); + this.setValueObject(null); + this.setValueIntArray(null); + super.reset(); + return this; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ModelWithAllPrivateFieldTypes_)) { + return false; + } + if (!super.equals(o)) { + return false; + } + ModelWithAllPrivateFieldTypes_ that = (ModelWithAllPrivateFieldTypes_) o; + if ((onModelBoundListener_epoxyGeneratedModel == null) != (that.onModelBoundListener_epoxyGeneratedModel == null)) { + return false; + } + if ((onModelUnboundListener_epoxyGeneratedModel == null) != (that.onModelUnboundListener_epoxyGeneratedModel == null)) { + return false; + } + if (getValueInteger() != null ? !getValueInteger().equals(that.getValueInteger()) : that.getValueInteger() != null) { + return false; + } + if (getValueShort() != that.getValueShort()) { + return false; + } + if (getValueLong() != that.getValueLong()) { + return false; + } + if (getValueList() != null ? !getValueList().equals(that.getValueList()) : that.getValueList() != null) { + return false; + } + if (getValueShortWrapper() != null ? !getValueShortWrapper().equals(that.getValueShortWrapper()) : that.getValueShortWrapper() != null) { + return false; + } + if (Double.compare(that.getValueDouble(), getValueDouble()) != 0) { + return false; + } + if (getValueChar() != that.getValueChar()) { + return false; + } + if (getValueInt() != that.getValueInt()) { + return false; + } + if (getValueDoubleWrapper() != null ? !getValueDoubleWrapper().equals(that.getValueDoubleWrapper()) : that.getValueDoubleWrapper() != null) { + return false; + } + if (getValueFloatWrapper() != null ? !getValueFloatWrapper().equals(that.getValueFloatWrapper()) : that.getValueFloatWrapper() != null) { + return false; + } + if (getValueBooleanWrapper() != null ? !getValueBooleanWrapper().equals(that.getValueBooleanWrapper()) : that.getValueBooleanWrapper() != null) { + return false; + } + if (getValueByteWrapper() != null ? !getValueByteWrapper().equals(that.getValueByteWrapper()) : that.getValueByteWrapper() != null) { + return false; + } + if (getValuebByte() != that.getValuebByte()) { + return false; + } + if (getValueLongWrapper() != null ? !getValueLongWrapper().equals(that.getValueLongWrapper()) : that.getValueLongWrapper() != null) { + return false; + } + if (getValueCharacter() != null ? !getValueCharacter().equals(that.getValueCharacter()) : that.getValueCharacter() != null) { + return false; + } + if (getValueString() != null ? !getValueString().equals(that.getValueString()) : that.getValueString() != null) { + return false; + } + if (Float.compare(that.getValueFloat(), getValueFloat()) != 0) { + return false; + } + if (isValueBoolean() != that.isValueBoolean()) { + return false; + } + if (!Arrays.equals(getValueObjectArray(), that.getValueObjectArray())) { + return false; + } + if (getValueObject() != null ? !getValueObject().equals(that.getValueObject()) : that.getValueObject() != null) { + return false; + } + if (!Arrays.equals(getValueIntArray(), that.getValueIntArray())) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (onModelBoundListener_epoxyGeneratedModel != null ? 1 : 0); + result = 31 * result + (onModelUnboundListener_epoxyGeneratedModel != null ? 1 : 0); + long temp; + result = 31 * result + (getValueInteger() != null ? getValueInteger().hashCode() : 0); + result = 31 * result + getValueShort(); + result = 31 * result + (int) (getValueLong() ^ (getValueLong() >>> 32)); + result = 31 * result + (getValueList() != null ? getValueList().hashCode() : 0); + result = 31 * result + (getValueShortWrapper() != null ? getValueShortWrapper().hashCode() : 0); + temp = Double.doubleToLongBits(getValueDouble()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + getValueChar(); + result = 31 * result + getValueInt(); + result = 31 * result + (getValueDoubleWrapper() != null ? getValueDoubleWrapper().hashCode() : 0); + result = 31 * result + (getValueFloatWrapper() != null ? getValueFloatWrapper().hashCode() : 0); + result = 31 * result + (getValueBooleanWrapper() != null ? getValueBooleanWrapper().hashCode() : 0); + result = 31 * result + (getValueByteWrapper() != null ? getValueByteWrapper().hashCode() : 0); + result = 31 * result + getValuebByte(); + result = 31 * result + (getValueLongWrapper() != null ? getValueLongWrapper().hashCode() : 0); + result = 31 * result + (getValueCharacter() != null ? getValueCharacter().hashCode() : 0); + result = 31 * result + (getValueString() != null ? getValueString().hashCode() : 0); + result = 31 * result + (getValueFloat() != +0.0f ? Float.floatToIntBits(getValueFloat()) : 0); + result = 31 * result + (isValueBoolean() ? 1 : 0); + result = 31 * result + Arrays.hashCode(getValueObjectArray()); + result = 31 * result + (getValueObject() != null ? getValueObject().hashCode() : 0); + result = 31 * result + Arrays.hashCode(getValueIntArray()); + return result; + } + + @Override + public String toString() { + return "ModelWithAllPrivateFieldTypes_{" + + "valueInteger=" + getValueInteger() + + ", valueShort=" + getValueShort() + + ", valueLong=" + getValueLong() + + ", valueList=" + getValueList() + + ", valueShortWrapper=" + getValueShortWrapper() + + ", valueDouble=" + getValueDouble() + + ", valueChar=" + getValueChar() + + ", valueInt=" + getValueInt() + + ", valueDoubleWrapper=" + getValueDoubleWrapper() + + ", valueFloatWrapper=" + getValueFloatWrapper() + + ", valueBooleanWrapper=" + getValueBooleanWrapper() + + ", valueByteWrapper=" + getValueByteWrapper() + + ", valuebByte=" + getValuebByte() + + ", valueLongWrapper=" + getValueLongWrapper() + + ", valueCharacter=" + getValueCharacter() + + ", valueString=" + getValueString() + + ", valueFloat=" + getValueFloat() + + ", valueBoolean=" + isValueBoolean() + + ", valueObjectArray=" + getValueObjectArray() + + ", valueObject=" + getValueObject() + + ", valueIntArray=" + getValueIntArray() + + "}" + super.toString(); + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithGetterWithParams.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithGetterWithParams.java new file mode 100755 index 0000000000..29f566c2e6 --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithGetterWithParams.java @@ -0,0 +1,18 @@ +package com.airbnb.epoxy; + +public class ModelWithPrivateFieldWithGetterWithParams extends EpoxyModel { + @EpoxyAttribute private int valueInt; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public int getValueInt(int param) { + return valueInt; + } + + public void setValueInt(int valueInt) { + this.valueInt = valueInt; + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithGetterWithWrongReturnType.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithGetterWithWrongReturnType.java new file mode 100755 index 0000000000..6ef88015c9 --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithGetterWithWrongReturnType.java @@ -0,0 +1,18 @@ +package com.airbnb.epoxy; + +public class ModelWithPrivateFieldWithGetterWithWrongReturnType extends EpoxyModel { + @EpoxyAttribute private int valueInt; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public boolean getValueInt() { + return false; + } + + public void setValueInt(int valueInt) { + this.valueInt = valueInt; + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithIsPrefixGetter.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithIsPrefixGetter.java new file mode 100755 index 0000000000..16272c04cb --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithIsPrefixGetter.java @@ -0,0 +1,18 @@ +package com.airbnb.epoxy; + +public class ModelWithPrivateFieldWithIsPrefixGetter extends EpoxyModel { + @EpoxyAttribute private boolean valueBoolean; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public boolean isValueBoolean() { + return valueBoolean; + } + + public void setValueBoolean(boolean valueBoolean) { + this.valueBoolean = valueBoolean; + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithPrivateGetter.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithPrivateGetter.java new file mode 100755 index 0000000000..7c57aac246 --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithPrivateGetter.java @@ -0,0 +1,18 @@ +package com.airbnb.epoxy; + +public class ModelWithPrivateFieldWithPrivateGetter extends EpoxyModel { + @EpoxyAttribute private int valueInt; + + @Override + protected int getDefaultLayout() { + return 0; + } + + private int getValueInt() { + return valueInt; + } + + public void setValueInt(int valueInt) { + this.valueInt = valueInt; + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithPrivateSetter.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithPrivateSetter.java new file mode 100755 index 0000000000..ff5a675c2b --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithPrivateSetter.java @@ -0,0 +1,18 @@ +package com.airbnb.epoxy; + +public class ModelWithPrivateFieldWithPrivateSetter extends EpoxyModel { + @EpoxyAttribute private int valueInt; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public int getValueInt() { + return valueInt; + } + + private void setValueInt(int valueInt) { + this.valueInt = valueInt; + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithSetterWithWrongParamType.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithSetterWithWrongParamType.java new file mode 100755 index 0000000000..b1cf36997e --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithSetterWithWrongParamType.java @@ -0,0 +1,17 @@ +package com.airbnb.epoxy; + +public class ModelWithPrivateFieldWithSetterWithWrongParamType extends EpoxyModel { + @EpoxyAttribute private int valueInt; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public int getValueInt() { + return valueInt; + } + + public void setValueInt(String valueString) { + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithSettterWithoutParams.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithSettterWithoutParams.java new file mode 100755 index 0000000000..4289187565 --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithSettterWithoutParams.java @@ -0,0 +1,18 @@ +package com.airbnb.epoxy; + +public class ModelWithPrivateFieldWithSettterWithoutParams extends EpoxyModel { + @EpoxyAttribute private int valueInt; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public int getValueInt() { + return valueInt; + } + + public void setValueInt() { + this.valueInt = 0; + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithStaticGetter.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithStaticGetter.java new file mode 100755 index 0000000000..8821eb1df3 --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithStaticGetter.java @@ -0,0 +1,18 @@ +package com.airbnb.epoxy; + +public class ModelWithPrivateFieldWithStaticGetter extends EpoxyModel { + @EpoxyAttribute private int valueInt; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public static int getValueInt() { + return 0; + } + + public void setValueInt(int valueInt) { + this.valueInt = valueInt; + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithStaticSetter.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithStaticSetter.java new file mode 100755 index 0000000000..958dc439c4 --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithStaticSetter.java @@ -0,0 +1,18 @@ +package com.airbnb.epoxy; + +public class ModelWithPrivateFieldWithStaticSetter extends EpoxyModel { + @EpoxyAttribute private int valueInt; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public int getValueInt() { + return valueInt; + } + + public static void setValueInt(int valueInt) { + + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithoutGetter.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithoutGetter.java new file mode 100755 index 0000000000..8f589d85bd --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithoutGetter.java @@ -0,0 +1,14 @@ +package com.airbnb.epoxy; + +public class ModelWithPrivateFieldWithoutGetter extends EpoxyModel { + @EpoxyAttribute private int valueInt; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public void setValueInt(int valueInt) { + this.valueInt = valueInt; + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateField.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithoutGetterAndSetter.java similarity index 61% rename from epoxy-processortest/src/test/resources/ModelWithPrivateField.java rename to epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithoutGetterAndSetter.java index b145f0220f..a6eba213dd 100755 --- a/epoxy-processortest/src/test/resources/ModelWithPrivateField.java +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithoutGetterAndSetter.java @@ -1,6 +1,6 @@ package com.airbnb.epoxy; -public class ModelWithPrivateField extends EpoxyModel { +public class ModelWithPrivateFieldWithoutGetterAndSetter extends EpoxyModel { @EpoxyAttribute private int valueInt; @Override diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithoutSetter.java b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithoutSetter.java new file mode 100755 index 0000000000..48da0471b8 --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateFieldWithoutSetter.java @@ -0,0 +1,14 @@ +package com.airbnb.epoxy; + +public class ModelWithPrivateFieldWithoutSetter extends EpoxyModel { + @EpoxyAttribute private int valueInt; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public int getValueInt() { + return valueInt; + } +} \ No newline at end of file diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateViewClickListener.java b/epoxy-processortest/src/test/resources/ModelWithPrivateViewClickListener.java new file mode 100644 index 0000000000..b512c62e28 --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateViewClickListener.java @@ -0,0 +1,25 @@ +package com.airbnb.epoxy; + +import android.view.View; +import android.view.View.OnClickListener; + +import com.airbnb.epoxy.EpoxyAttribute.Option; + +import static com.airbnb.epoxy.EpoxyAttribute.Option.DoNotHash; + +public class ModelWithPrivateViewClickListener extends EpoxyModel { + @EpoxyAttribute(DoNotHash) private View.OnClickListener clickListener; + + @Override + protected int getDefaultLayout() { + return 0; + } + + public OnClickListener getClickListener() { + return clickListener; + } + + public void setClickListener(OnClickListener clickListener) { + this.clickListener = clickListener; + } +} diff --git a/epoxy-processortest/src/test/resources/ModelWithPrivateViewClickListener_.java b/epoxy-processortest/src/test/resources/ModelWithPrivateViewClickListener_.java new file mode 100644 index 0000000000..e708fd8229 --- /dev/null +++ b/epoxy-processortest/src/test/resources/ModelWithPrivateViewClickListener_.java @@ -0,0 +1,196 @@ +package com.airbnb.epoxy; + +import android.support.annotation.LayoutRes; +import android.view.View; +import java.lang.CharSequence; +import java.lang.Object; +import java.lang.Override; +import java.lang.String; + +/** + * Generated file. Do not modify! */ +public class ModelWithPrivateViewClickListener_ extends ModelWithPrivateViewClickListener implements GeneratedModel { + private OnModelBoundListener onModelBoundListener_epoxyGeneratedModel; + + private OnModelUnboundListener onModelUnboundListener_epoxyGeneratedModel; + + private OnModelClickListener clickListener_epoxyGeneratedModel; + + public ModelWithPrivateViewClickListener_() { + super(); + } + + @Override + public void handlePreBind(final EpoxyViewHolder holder, final Object object) { + if (clickListener_epoxyGeneratedModel != null) { + super.setClickListener(new View.OnClickListener() { + // Save the original click listener so if it gets changed on + // the generated model this click listener won't be affected + // if it is still bound to a view. + private final OnModelClickListener clickListener_epoxyGeneratedModel = ModelWithPrivateViewClickListener_.this.clickListener_epoxyGeneratedModel; + public void onClick(View v) { + clickListener_epoxyGeneratedModel.onClick(ModelWithPrivateViewClickListener_.this, object, v, + holder.getAdapterPosition()); + } + public int hashCode() { + // Use the hash of the original click listener so we don't change the + // value by wrapping it with this anonymous click listener + return clickListener_epoxyGeneratedModel.hashCode(); + } + }); + } + } + + @Override + public void handlePostBind(final EpoxyViewHolder holder, final Object object) { + if (onModelBoundListener_epoxyGeneratedModel != null) { + onModelBoundListener_epoxyGeneratedModel.onModelBound(this, object); + } + } + + /** + * Register a listener that will be called when this model is bound to a view. + *

+ * The listener will contribute to this model's hashCode state per the {@link + * com.airbnb.epoxy.EpoxyAttribute.Option#DoNotHash} rules. + *

+ * You may clear the listener by setting a null value, or by calling {@link #reset()} */ + public ModelWithPrivateViewClickListener_ onBind(OnModelBoundListener listener) { + this.onModelBoundListener_epoxyGeneratedModel = listener; + return this; + } + + @Override + public void unbind(Object object) { + super.unbind(object); + if (onModelUnboundListener_epoxyGeneratedModel != null) { + onModelUnboundListener_epoxyGeneratedModel.onModelUnbound(this, object); + } + } + + /** + * Register a listener that will be called when this model is unbound from a view. + *

+ * The listener will contribute to this model's hashCode state per the {@link + * com.airbnb.epoxy.EpoxyAttribute.Option#DoNotHash} rules. + *

+ * You may clear the listener by setting a null value, or by calling {@link #reset()} */ + public ModelWithPrivateViewClickListener_ onUnbind(OnModelUnboundListener listener) { + this.onModelUnboundListener_epoxyGeneratedModel = listener; + return this; + } + + /** + * Set a click listener that will provide the parent view, model, and adapter position of the clicked view. This will clear the normal View.OnClickListener if one has been set */ + public ModelWithPrivateViewClickListener_ clickListener(final OnModelClickListener clickListener) { + super.setClickListener(null); + this.clickListener_epoxyGeneratedModel = clickListener; + return this; + } + + public ModelWithPrivateViewClickListener_ clickListener(View.OnClickListener clickListener) { + this.setClickListener(clickListener); + this.clickListener_epoxyGeneratedModel = null; + return this; + } + + public View.OnClickListener clickListener() { + return getClickListener(); + } + + @Override + public ModelWithPrivateViewClickListener_ id(long id) { + super.id(id); + return this; + } + + @Override + public ModelWithPrivateViewClickListener_ id(CharSequence key) { + super.id(key); + return this; + } + + @Override + public ModelWithPrivateViewClickListener_ id(CharSequence key, long id) { + super.id(key, id); + return this; + } + + @Override + public ModelWithPrivateViewClickListener_ layout(@LayoutRes int arg0) { + super.layout(arg0); + return this; + } + + @Override + public ModelWithPrivateViewClickListener_ show() { + super.show(); + return this; + } + + @Override + public ModelWithPrivateViewClickListener_ show(boolean show) { + super.show(show); + return this; + } + + @Override + public ModelWithPrivateViewClickListener_ hide() { + super.hide(); + return this; + } + + @Override + public ModelWithPrivateViewClickListener_ reset() { + onModelBoundListener_epoxyGeneratedModel = null; + onModelUnboundListener_epoxyGeneratedModel = null; + this.setClickListener(null); + clickListener_epoxyGeneratedModel = null; + super.reset(); + return this; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ModelWithPrivateViewClickListener_)) { + return false; + } + if (!super.equals(o)) { + return false; + } + ModelWithPrivateViewClickListener_ that = (ModelWithPrivateViewClickListener_) o; + if ((onModelBoundListener_epoxyGeneratedModel == null) != (that.onModelBoundListener_epoxyGeneratedModel == null)) { + return false; + } + if ((onModelUnboundListener_epoxyGeneratedModel == null) != (that.onModelUnboundListener_epoxyGeneratedModel == null)) { + return false; + } + if ((getClickListener() == null) != (that.getClickListener() == null)) { + return false; + } + if ((clickListener_epoxyGeneratedModel == null) != (that.clickListener_epoxyGeneratedModel == null)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (onModelBoundListener_epoxyGeneratedModel != null ? 1 : 0); + result = 31 * result + (onModelUnboundListener_epoxyGeneratedModel != null ? 1 : 0); + result = 31 * result + (getClickListener() != null ? 1 : 0); + result = 31 * result + (clickListener_epoxyGeneratedModel != null ? 1 : 0); + return result; + } + + @Override + public String toString() { + return "ModelWithPrivateViewClickListener_{" + + "clickListener=" + getClickListener() + + "}" + super.toString(); + } +}