jaxb2-rich-contract-plugin
jaxb2-rich-contract-plugin copied to clipboard
Chained builders for list members
I have recently updated to the latest version of the plugin to allow me to use the new functionality in #33 for fluent builders. I am really enjoying using it and it has saved me so much work in manually creating the builders and data objects to interact with our API.
This has been really helpful for chaining builders together and allowing me to take an existing object, convert it to a builder, modify it then build back to an immutable object.
I am however seeing issues for members which are lists. The main XML schema we use has a lot of these and unfortunately, the chained builders don't seem to work, only the standard withA(A) type methods and addA() methods which will initialise a builder to add a new object to the list.
The issue comes when I want to take an existing data object and update one of the objects in the list with new values. The current methods provided do not give me any good way to chain my way through and modify these existing objects in the list using the newCopyBuilder() of the parent object without having to manually create builders for each of the list objects and provide them back to the builder without any of the chaining that has been so useful.
Is there any way to extend the current chained builders to allow us to use list-based members in the same way that single ones do, whereby we could call withA() and it initialises a builder for each member of the list and provide a way to access these builders using the standard chained builder idioms?
I'm guessing that this would involve some custom list implementation which would allow a List of builders to implement Builder<Void> and provide the .end() method to allow it to chain back to the parent object.
Hope this makes sense, can give a more concrete example if not.
Pondered this one overnight and I think some kind of BuilderList class might do the trick. This would implement Buildable and provide the chained end() method and a reference to the parent builder.
This should allow the following kind of syntax:
builder.withListMember()
.get(0).withMember(memberValue).withOther(otherValue).end()
.get(1).withMember(nextValue).end()
.end()
.withB(bValue)
.end()
Not overly keen on the explicit gets, I'm wondering with the java 8 support whether there could be a get method which takes a function to allow getting an item from the list conditionally?
e.g
builder.withListMember()
.getWhere(item -> item.getMember().equals("value")).withMember("newValue").end()
This would have the overhead of converting the builder into an object, but the current case requires doing this same overhead manually so I don't see it would be a massive issue.
Any thoughts?
Hi Dave,
such a pattern would definitely be possible to implement, and it would also make sense IMHO. For consistency, however, the question arises whether it would also make sense to add „get“ methods for already existing sub-builders of single-multiplicity properties, in order to be able to complete building of partially built or copy-initialized parts of the builder graph.
Because this whole project started out from the special requirements of a business application, however, there was no need for such features up until now. I’ll think about how to implement it. The function to retrieve an item from the builder list also shouldn’t be a problem, but then again it would also be consistent to implement a „stream()“ on the list property builder to give access to the items…
Mirko
Am 2019-01-09 um 10:33 schrieb Dave Birch [email protected]:
Pondered this one overnight and I think some kind of BuilderList class might do the trick. This would implement Buildable and provide the chained end() method and a reference to the parent builder.
This should allow the following kind of syntax:
builder.withListMember() .get(0).withMember(memberValue).withOther(otherValue).end() .get(1).withMember(nextValue).end() .end() .withB(bValue) .end()
Not overly keen on the explicit gets, I'm wondering with the java 8 support whether there could be a get method which takes a function to allow getting an item from the list conditionally?
e.g
builder.withListMember() .getWhere(item -> item.getMember().equals("value")).withMember("newValue").end()
This would have the overhead of converting the builder into an object, but the current case requires doing this same overhead manually so I don't see it would be a massive issue.
Any thoughts?
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.
Thanks for the response, it's appreciated :)
I initially misunderstood what you mean about the getters, but as I understand it now it would allow users to get the values of the properties currently stored by the builder, my worry is that accidentally calling one of these methods would stop the builder from chaining and would only really be useful within the functions that I've suggested.
Streaming the list values also sounds like it could be an idea. My worry was that a stream().filter(function).findFirst().orElseThrow() might be a little verbose for a fluent chained builder and that wrapping up this code within a utility method might make the builders more readable.
More than happy to help out with the implementation of this if you'd like once you've had a think about how you would like it implemented. I'm quite keen to get this functionality soonish before my team add all sorts of horrible workarounds to our codebase to work around this feature not being present :D
Hi everybody, I also had the need to update a builder, especially the existing builders of a collection property. There was no way to iterate over those and update them. Although the BuilderList idea presented earlier looks very appealing as it supports the fluent builder approach I found that for this operation of updating an exiting builder I do not needed it. I mostly need to traverse the builder in depth until I find the right sub-builder and to update some property. I do not need to be able to 'end' that builder and jump back to the parent to update it. If I need to update the parent then I can do it during the traversal. So I realized that adding a getter for each builder field would solve my problem. With these getters I can read all existing builder fields, both the simple or the collection ones, which allows me to implement any kind of search/filter and update builder operation. I implemented the generation of those getters under a new fluent-builder/getters option which is off by default. Please have a look on this pull request and let me know if it is ok: https://github.com/mklemm/jaxb2-rich-contract-plugin/pull/73