-
Notifications
You must be signed in to change notification settings - Fork 729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow to annotate private fields based on their getters and setters #144
Conversation
@@ -142,6 +155,43 @@ private boolean isFieldPackagePrivate(Element attribute) { | |||
} | |||
|
|||
/** | |||
* Checks if the given private field has getter and setter for access to it | |||
*/ | |||
private void findGetterAndSetterForPrivateField(ErrorLogger errorLogger) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To simplify, what if we assumed/enforced that the getter/setter have the same name as the field. Our generated getter/setter will already share that name so it seems like it would be a good thing for consistency anyway. Does this make sense in kotlin?
I previously made a ProcessorUtilsgetMethodOnClass
method that you may be able to reuse to simplify this, although you may want to add some modifier checking into that method (to check !static, etc).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing is that we can't enforce it since Kotlin generate automatically getters and setters for properties with this get/set naming pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah makes sense. oh well, no big deal
+ " // value by wrapping it with this anonymous click listener\n" | ||
+ " return $L.hashCode();\n" | ||
+ " }\n" | ||
+ " });\n", attribute.setter(), viewClickListenerType, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is using the setter vs assigning the field directly the only difference? Maybe we can keep the main code block the same, but assign it to a local variable.
Then in an if/else we can either call the super setter or set the super field. That way we could reuse most of the code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, just wasn't sure how to proceed with it. I managed to solve it. Will include into next commit.
@@ -623,6 +656,41 @@ private void addEqualsLineForType(Builder builder, boolean useObjectHashCode, Ty | |||
} | |||
} | |||
|
|||
private void addEqualsLineForTypeUsingGetter(Builder builder, boolean useObjectHashCode, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid duplicating hash/equals methods, what if we made a little method like attributeGetterCode
and attributeSetterCode
, which would so something like
if(isPrivateAttribute){
return attribute.name + "()"; // Assuming we enforce setter name has to equal field name
} else {
return attribute.name;
}
Setter might be trickier but maybe possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could also be a method on the attribute info -> attributeInfo.getAccessorCode()
I think this approach could clean up the code in all the usages above too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've made it slightly different. addEqualsLineForTypeUsingGetter
is not needed anymore. Same for hash code generation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks! I see what you mean about the code duplication, I think we can figure out some ways to improve it, but this seems like a great start. I gave it a brief look but will try to spend more time on it soon.
Not having the right compilation error is weird. Probably related to my changes to group all errors at the end. I'll try running these tests locally and see if I can debug. I was thinking recently that we could make a script that updates failing processor tests with the actual output. The script could take the filepath of the report, like This would be very helpful when we change a small thing in the processor that affects all processor tests. We know the actual code is right, but would have to manually update the test code. I know you've had to do that in the past :P, and I 've been wasting a lot of time on it recently with my refactor. |
Fyi #145 will cause a small merge conflict, but it is just adding a view param to the model click listener in the generated code in tests. If you prefer I can wait to merge it until after this PR is merged |
Go ahead! I'll resolve a merge conflict. It's not a big deal. |
Thanks. I'm gonna try my hand at a ruby script to automatically update processor tests, so maybe don't try changing your tests for now (if you change processor code) |
I think I have the script working (albeit it's ugly :P)
you would first run You just provide the module name of the tests, like: I've only tested briefly. I need to go through and clean it up, but if you want to try it for you own usage to save some time, go for it! I'm not great with ruby, so if you see opportunity for improvement, please go for it. I'll put this up as a PR later when I have time. It also assumes it is being run from the root directory of the repo |
ExecutableElement method = (ExecutableElement) element; | ||
String methodName = method.getSimpleName().toString(); | ||
// check if it is a valid getter | ||
if (methodName.equalsIgnoreCase(String.format("get%s", name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this not be equalsIgnoreCase
since with a different case it would be a different method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, it was just the easiest approach. I'll handle it properly. Thanks for pointing out!
String methodName = method.getSimpleName().toString(); | ||
// check if it is a valid getter | ||
if (methodName.equalsIgnoreCase(String.format("get%s", name)) | ||
|| methodName.equalsIgnoreCase(String.format("is%s", name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah your tests are failing because your order of operations for the logic here is messed up. You need ( )
around your ||
operation
(methodName.equalsIgnoreCase(String.format("get%s", name))
|| methodName.equalsIgnoreCase(String.format("is%s", name)))
Tricky!
Btw I recently learned how to debug annotation processing and it is very helpful. I used it to figure this out https://blog.xmartlabs.com/2016/03/28/Debugging-an-Annotator-Processor-in-your-project/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wow, thanks! That makes sense now. Yeah, I've seen this article, but I was pretty sure that the code is alright and the issue was in errors handling. Shame on me :P
} | ||
|
||
@Test | ||
public void modelWithViewPrivateClickListener() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding so many tests!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem, I am jus trying to cover every possible case. This is the purpose of tests after all :)
# Conflicts: # epoxy-processor/src/main/java/com/airbnb/epoxy/GeneratedModelWriter.java
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks!
I'll be sure to highlight this in the 2.0 release :)
Glad that we were able to deal with it so fast :) |
Can you give me an example of how this is meant to work @geralt-encore ? Using version 2.1.0 gives me the error mentioned in this thread with my code:
|
@jbmlaird |
#133
Well, it took much more that I thought initially. Now it is possible to apply
@EpoxyAttribute
directly on Kotlin properties without@JvmField
.There is quite some code duplication with these changes, but I didn't find a better solution. Please, feel free to suggest any improvements, since I am not really happy with it myself.
Also, there is a weird issue that a couple of my tests are failing. I am checking that compilation fails with a specific error, but it contains only final compilation error in generated code without any mentions of the initial error. And I am pretty sure that I logged it correctly since the code I am checking with these tests is pretty trivial. So it would be nice if you could help me to deal with it.