You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Paul King (JIRA)" <ji...@apache.org> on 2017/05/15 01:22:04 UTC

[jira] [Comment Edited] (GROOVY-8186) Builder ExternalStrategy constructors have trouble with private fields

    [ https://issues.apache.org/jira/browse/GROOVY-8186?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16009898#comment-16009898 ] 

Paul King edited comment on GROOVY-8186 at 5/15/17 1:21 AM:
------------------------------------------------------------

For case (1), which is when the builder class and buildee class are in the same compilation unit, you are correct that JavaBean properties aren't considered, just standard Groovy properties. We probably should change this. I suspect what we do for {{@ToString}}, where the underlying transform calls {{BeanUtils.getAllProperties}} to get the complete set of properties, is the way to go.

For case (2), it isn't really Groovy vs Java but whether the class is pre-compiled or not. When pre-compiled, {{@Builder}} uses standard JavaBean introspection to find the properties. If the parameter type of the setter doesn't match with the return type of the getter, the property is deemed read-only from a JavaBean perspective (and there just happens to be a setter-like named method also existing with a different parameter type):
{code}
def personClass = new GroovyShell().evaluate '''
class GroovyPerson {
    private Set<String> pets
    //Collection<String> getPets() { pets }
    Set<String> getPets() { pets }
    //void setPets(Set<String> pets) {
    void setPets(Collection<String> pets) {
        this.pets = pets == null ? Collections.emptySet() : new LinkedHashSet<String>(pets)
    }
}
GroovyPerson
'''
java.beans.Introspector.getBeanInfo(personClass).propertyDescriptors.each { pd ->
    if (!['class', 'metaClass'].contains(pd.name))
        println "$pd.name $pd.propertyType.name $pd.writeMethod"
}
{code}
The null {{writeMethod}} indicates a read-only property. Uncomment either of the commented out lines (and remove the appropriate existing line) to see the property reappear.
I suspect we don't want to change that but I haven't checked what case (1) does in that situation yet - if we were to fix it as per above.


was (Author: paulk):
For case (1), which is when the builder class and buildee class are in the same compilation unit, you are correct that JavaBean properties aren't considered, just standard Groovy properties. We probably should change this. I suspect what we do for {{@ToString}}, where the underlying transform calls {{BeanUtils.getAllProperties}} to get the complete set of properties, is the way to go.

For case (2), it isn't really Groovy vs Java but whether the class is pre-compiled or not. When pre-compiled, {{@Builder}} uses standard JavaBean introspection to find the properties. If the parameter type of the setter doesn't match with the return type of the getter, the property is deemed read-only from a JavaBean perspective (and there just happens to be a setter-like named method also existing with a different parameter type):
{code}
def personClass = new GroovyShell().evaluate '''
class GroovyPerson {
    private Set<String> pets
    //Collection<String> getPets() { pets }
    Set<String> getPets() { pets }
    //void setPets(Set<String> pets) {
    void setPets(Collection<String> pets) {
        this.pets = pets == null ? Collections.emptySet() : new LinkedHashSet<String>(pets)
    }
}
GroovyPerson
'''
java.beans.Introspector.getBeanInfo(personClass).propertyDescriptors.each { pd ->
    if (!['class', 'metaClass'].contains(pd.name))
        println "$pd.name $pd.propertyType.name $pd.writeMethod"
}
{code}
The null {{writeMethod}} indicates a read-only property. Uncomment either of the commented out lines to see the property reappear.
I suspect we don't want to change that but I haven't checked what case (1) does in that situation yet.

> Builder ExternalStrategy constructors have trouble with private fields
> ----------------------------------------------------------------------
>
>                 Key: GROOVY-8186
>                 URL: https://issues.apache.org/jira/browse/GROOVY-8186
>             Project: Groovy
>          Issue Type: Bug
>    Affects Versions: 2.4.x, 2.5.x
>            Reporter: Jason Terhune-Wold
>            Priority: Minor
>
> An undocumented feature of @Builder is that the builder's constructor can set default values for the fields. See http://stackoverflow.com/questions/35066664/default-values-in-groovy-builder-ast
> I tried creating an ExternalStrategy @Builder annotation for [BaseClientDetails|http://docs.spring.io/spring-security/oauth/apidocs/org/springframework/security/oauth2/provider/client/BaseClientDetails.html] with a constructor that set a default scope. I received a MissingMethodException exception, which I thought was odd.
> I added some tests to BuilderTransformTest that demonstrate apparent problems using the external builder in two situations:
> 1) A groovy class with private fields and manually defined setters.
> 2) A java class with a private Set field and a setter that takes a Collection
> The tests are here: https://github.com/jterhune/groovy/commit/5b2eb74cc8184235b5235b7fd4c80b241799bbc5
> I noticed this in 2.4.x. My tests fail in the 2.5.x branch also.



--
This message was sent by Atlassian JIRA
(v6.3.15#6346)