Skip to content
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 code generation to use nested method invocations #1043

Closed
onobc opened this issue Dec 2, 2019 · 10 comments
Closed

Allow code generation to use nested method invocations #1043

onobc opened this issue Dec 2, 2019 · 10 comments
Assignees
Milestone

Comments

@onobc
Copy link
Contributor

onobc commented Dec 2, 2019

I do not currently see a way to generate the following Java code:

Builder.newBuilder().setId("example-dependency");

I can produce the following multi-line version

Builder builder = Builder.newBuilder();
builder.setId("example-dependency");

but in reality the fluent call chain I am trying to generate is like 9 calls long and I don't want to produce this multi-statement style code.

This limitation stems from the fact that JavaMethodInvocation target can only be a String class name. I would like to add the ability to have its target optionally be another JavaMethodInvocation.

Example code:

new JavaMethodInvocation(
	new JavaMethodInvocation("com.example.Builder", "newBuilder")),
	"setId", "example-dependeny"));
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Dec 2, 2019
@snicoll
Copy link
Contributor

snicoll commented Mar 17, 2020

@bono007 thanks for the report and sorry for the delay. I've had a look to what Javapoet does and the more I look at it and the more I think we should have some sort of support for that. The indirection that we currently have is based on the idea that it is a contribution model that can be customized externally but that doesn't apply to a method body.

I don't know how much effort it would take to provide some basic utilities to write code in text but I think it's worth exploring.

@snicoll snicoll added type: enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Mar 17, 2020
@onobc
Copy link
Contributor Author

onobc commented Mar 17, 2020

t hanks for the report and sorry for the delay.

@snicoll - no worries. I forgot about the issue anyways :)

I don't know how much effort it would take to provide some basic utilities to write code in text but I think it's worth exploring.

Sounds fun. I will do some digging this weekend and report back what I find.

@onobc
Copy link
Contributor Author

onobc commented Mar 23, 2020

I started looking into this briefly @snicoll and Javapoet does seem promising. Its pretty slick. I am continuing to look into it - just giving a bit of status.

@onobc
Copy link
Contributor Author

onobc commented Mar 30, 2020

@snicoll

JavaPoet allows generating Java programatically via their API just like the Initiaizr io.spring.initializr.generator.language.java package does. However, JavaPoet is more flexible and allows creating pretty much any code whereas the current Initializr offering is limited.

When you say

write code in text

what were you envisioning?

Were you thinking of allowing something along the lines of a JavaMethodImplementation where it would be free from text of the method?

Also, one thing to note is that whatever route we go down w/ Poet, we would need to cover Kotlin and Groovy as well. There is a KotlinPoet for the former and I believe we could use JavaPoet for Groovy (not 100% sure though).

@snicoll
Copy link
Contributor

snicoll commented Mar 30, 2020

what were you envisioning?

Thanks for asking. I can't really say without spending some time investigating. My plan was to open up the API a bit so that other forms of writer could be provided. So something where you'd define the type and still offer a way to add annotations on the fly via a customizer but where methods could be provided by another form.

We don't want a hard dependency on Javapoet or any other library so that would work in the form of a strategy interface where we'd provide a JavaPoet implementation (and therefore an optional dependency on it).

From that perspective, I don't think that the fact we have to support all languages is necessarily true. For languages where such third party library does not exist, we'd stick with the limitation of the current implementation (and/or we would improve it).

At the end of the day, custom instances that need to write "complex" java code in Java (or Kotlin) could simply create a ProjectContributor that does that using JavaPoet directly and not rely on any of the code writer API of this project.

@onobc
Copy link
Contributor Author

onobc commented Apr 6, 2020

Hi @snicoll

I spent some time this weekend digging into this. I am not seeing a clear worthwhile path forward as of now. Here is where I am at..

So something where you'd define the type and still offer a way to add annotations on the fly via a customizer but where methods could be provided by another form.

The JavaPoet writer API is very closed and it does an all or nothing render of the entire source. I was hoping that it had a more fine-grained API that we could use to write, say only the methods, and have Initializr be able to combine/merge this to its written output. This would allow JavaPoet to more or less "contribute" the method source generation for us. Otherwise we would end up having to create an writer adapter from Initializr source model to JavaPoet source model and let JavaPoet handle the writing. While that would work it definitely makes we question if its worth it. I feel like it would be less complex to simply add the ability in io.spring.initializr.generator.language.java.JavaMethodDeclaration#JavaMethodDeclaration to add JavaFreeTextStatementBlock (terrible name) or something like that.

custom instances that need to write "complex" java code in Java (or Kotlin) could simply create a ProjectContributor that does that using JavaPoet directly and not rely on any of the code writer API of this project.

This case we can handle today w/o any modifications I believe.

@snicoll
Copy link
Contributor

snicoll commented Apr 16, 2020

The JavaPoet writer API is very closed and it does an all or nothing render of the entire source. I was hoping that it had a more fine-grained API that we could use to write, say only the methods, and have Initializr be able to combine/merge this to its written output.

Well we could build something around that ourselves. There are cases like @Configuration where the ability to add methods via a customizer is interesting and you could do that quite easily I think. A MethodSpec can be created in isolation and can be collected with a callback before the JavaFile is created.

I feel like it would be less complex to simply add the ability

The more I look at this and the less I am convinced by that. Once the question of a complex body is resolved, the next step is to have complex type, support of generics and what not. We'll end up re-implementing what this library already does.

This case we can handle today w/o any modifications I believe.

You can write any resource any way you like, so, yes. I was talking about an out-of-the-box support of Javapoet in the library in an optional fashion. That would mean some high-level interface you can implement and the code would be generated automatically via javapoet.

I would need more time to explore those options to make up my mind. I would recommend using Javapoet directly in your own project in the meantime. Let us know how it goes and we can revisit this one when time permits.

@onobc
Copy link
Contributor Author

onobc commented Apr 19, 2020

HI @snicoll ,
Thanks for the clarifications.

I would recommend using Javapoet directly in your own project in the meantime. Let us know how it goes and we can revisit this one when time permits.

I will do this and as I go through it I am sure I will learn a few things and I will keep the comments above in mind. I will let you know what I find.

Thanks

@ggerbaud
Copy link
Contributor

Hi @snicoll and @bono007

I read this discussion and I understand that making this generator as powerful as Javapoet is not the point.
But I add this feature (and exception throwing) for my own needs so I share it through this contribution (#1086 )

@snicoll snicoll self-assigned this Jun 7, 2023
@snicoll snicoll added this to the 0.20.0 milestone Jun 7, 2023
@snicoll snicoll changed the title Allow Java code generation to use nested method invocations Allow code generation to use nested method invocations Jun 7, 2023
@snicoll snicoll closed this as completed in 8acbad5 Jun 7, 2023
@snicoll
Copy link
Contributor

snicoll commented Jun 7, 2023

With the need of writing more complex statements ourselves, we've spent a bit of time investigating how to integrate JavaPoet as a contribution. Given the challenge of supporting three languages with a consistent and controller formatting, it turned out to be the wrong path for us. However, we've introduced a CodeBlock class, similar to the one of JavaPoet and focused on the core feature that we need.

The original need of writing:

Builder.newBuilder().setId("example-dependency");

can be accomplished as follows:

CodeBlock.ofStatement("$T.newBuilder().setId($S)", Builder.class, "example-dependency");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants