Closed Randgalt closed 2 years ago
cc @sesamzoo and @agentgt
FYI - for the standard @RecordBuilder
you get:
// Auto generated by io.soabase.recordbuilder.core.RecordBuilder: https://github.com/Randgalt/record-builder
package io.soabase.recordbuilder.test.typeuse;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Stream;
import javax.annotation.processing.Generated;
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public class MyRecordBuilder {
private String nonNullS;
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
private MyRecordBuilder() {
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
private MyRecordBuilder(String nonNullS) {
this.nonNullS = nonNullS;
}
/**
* Static constructor/builder. Can be used instead of new MyRecord(...)
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public static MyRecord MyRecord(@TypeUseNonNull String nonNullS) {
return new MyRecord(nonNullS);
}
/**
* Return a new builder with all fields set to default Java values
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public static MyRecordBuilder builder() {
return new MyRecordBuilder();
}
/**
* Return a new builder with all fields set to the values taken from the given record instance
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public static MyRecordBuilder builder(MyRecord from) {
return new MyRecordBuilder(from.nonNullS());
}
/**
* Return a "with"er for an existing record instance
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public static MyRecordBuilder.With from(MyRecord from) {
return new _FromWith(from);
}
/**
* Return a stream of the record components as map entries keyed with the component name and the value as the component value
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public static Stream<Map.Entry<String, Object>> stream(MyRecord record) {
return Stream.of(new AbstractMap.SimpleImmutableEntry<>("nonNullS", record.nonNullS()));
}
/**
* Return a new record instance with all fields set to the current values in this builder
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public MyRecord build() {
return new MyRecord(nonNullS);
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@Override
public String toString() {
return "MyRecordBuilder[nonNullS=" + nonNullS + "]";
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@Override
public int hashCode() {
return Objects.hash(nonNullS);
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@Override
public boolean equals(Object o) {
return (this == o) || ((o instanceof MyRecordBuilder r)
&& Objects.equals(nonNullS, r.nonNullS));
}
/**
* Set a new value for the {@code nonNullS} record component in the builder
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public MyRecordBuilder nonNullS(@TypeUseNonNull String nonNullS) {
this.nonNullS = nonNullS;
return this;
}
/**
* Return the current value for the {@code nonNullS} record component in the builder
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@TypeUseNonNull
public String nonNullS() {
return nonNullS;
}
/**
* Add withers to {@code MyRecord}
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public interface With {
/**
* Return the current value for the {@code nonNullS} record component in the builder
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@TypeUseNonNull
String nonNullS();
/**
* Return a new record builder using the current values
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
default MyRecordBuilder with() {
return new MyRecordBuilder(nonNullS());
}
/**
* Return a new record built from the builder passed to the given consumer
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
default MyRecord with(Consumer<MyRecordBuilder> consumer) {
MyRecordBuilder builder = with();
consumer.accept(builder);
return builder.build();
}
/**
* Return a new instance of {@code MyRecord} with a new value for {@code nonNullS}
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
default MyRecord withNonNullS(@TypeUseNonNull String nonNullS) {
return new MyRecord(nonNullS);
}
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
private static final class _FromWith implements MyRecordBuilder.With {
private final MyRecord from;
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
private _FromWith(MyRecord from) {
this.from = from;
}
@Override
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public String nonNullS() {
return from.nonNullS();
}
}
}
For RecordBuilderFull
you get:
// Auto generated by io.soabase.recordbuilder.core.RecordBuilder: https://github.com/Randgalt/record-builder
package io.soabase.recordbuilder.test.typeuse;
import io.soabase.recordbuilder.core.RecordBuilderGenerated;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
import javax.annotation.processing.Generated;
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@RecordBuilderGenerated
public class MyFullRecordBuilder {
private String nonNullS;
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
private MyFullRecordBuilder() {
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
private MyFullRecordBuilder(String nonNullS) {
this.nonNullS = nonNullS;
}
/**
* Static constructor/builder. Can be used instead of new MyFullRecord(...)
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public static MyFullRecord MyFullRecord(@TypeUseNonNull String nonNullS) {
return new MyFullRecord(nonNullS);
}
/**
* Return a new builder with all fields set to default Java values
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public static MyFullRecordBuilder builder() {
return new MyFullRecordBuilder();
}
/**
* Return a new builder with all fields set to the values taken from the given record instance
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public static MyFullRecordBuilder builder(MyFullRecord from) {
return new MyFullRecordBuilder(from.nonNullS());
}
/**
* Return a "with"er for an existing record instance
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public static MyFullRecordBuilder.With from(MyFullRecord from) {
return new _FromWith(from);
}
/**
* Return a stream of the record components as map entries keyed with the component name and the value as the component value
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public static Stream<Map.Entry<String, Object>> stream(MyFullRecord record) {
return Stream.of(new AbstractMap.SimpleImmutableEntry<>("nonNullS", record.nonNullS()));
}
/**
* Return a new record instance with all fields set to the current values in this builder
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public MyFullRecord build() {
return new MyFullRecord(nonNullS);
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@Override
public String toString() {
return "MyFullRecordBuilder[nonNullS=" + nonNullS + "]";
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@Override
public int hashCode() {
return Objects.hash(nonNullS);
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@Override
public boolean equals(Object o) {
return (this == o) || ((o instanceof MyFullRecordBuilder r)
&& Objects.equals(nonNullS, r.nonNullS));
}
/**
* Set a new value for the {@code nonNullS} record component in the builder
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public MyFullRecordBuilder nonNullS(@TypeUseNonNull String nonNullS) {
this.nonNullS = nonNullS;
return this;
}
/**
* Return the current value for the {@code nonNullS} record component in the builder
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@TypeUseNonNull
public String nonNullS() {
return nonNullS;
}
/**
* Add withers to {@code MyFullRecord}
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@RecordBuilderGenerated
public interface With {
/**
* Return the current value for the {@code nonNullS} record component in the builder
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@TypeUseNonNull
String nonNullS();
/**
* Return a new record builder using the current values
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
default MyFullRecordBuilder with() {
return new MyFullRecordBuilder(nonNullS());
}
/**
* Return a new record built from the builder passed to the given consumer
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
default MyFullRecord with(java.util.function.Consumer<MyFullRecordBuilder> consumer) {
MyFullRecordBuilder builder = with();
consumer.accept(builder);
return builder.build();
}
/**
* Return a new instance of {@code MyFullRecord} with a new value for {@code nonNullS}
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
default MyFullRecord withNonNullS(@TypeUseNonNull String nonNullS) {
return new MyFullRecord(nonNullS);
}
/**
* Map record components into a new object
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
default <R> R map(Function<R> proc) {
return proc.apply(nonNullS());
}
/**
* Perform an operation on record components
*/
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
default void accept(Consumer proc) {
proc.apply(nonNullS());
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@FunctionalInterface
interface Function<R> {
R apply(@TypeUseNonNull String nonNullS);
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@FunctionalInterface
interface Consumer {
void apply(@TypeUseNonNull String nonNullS);
}
}
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
@RecordBuilderGenerated
private static final class _FromWith implements MyFullRecordBuilder.With {
private final MyFullRecord from;
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
private _FromWith(MyFullRecord from) {
this.from = from;
}
@Override
@Generated("io.soabase.recordbuilder.core.RecordBuilder")
public String nonNullS() {
return from.nonNullS();
}
}
}
@sesamzoo and @agentgt any comments before I merge?
I’ll have a look Monday. Sorry I was sick the last few days.
@Randgalt, with this PR my example from #113 works as expected.
@Randgalt it looks right. I have not pulled the code yet and tested things like arrays and inner classes but it looks correct. Even if it doesn't handle those cases yet this looks like the correct behavior of propagating the type annotations.
BTW you might want to test and or be careful with the Eclipse compiler annotation processor. IIRC it has an issue where it will propagate @NonNull
-like TYPE_USE annotations even if they are not on the type! Basically the nonnull analysis part of the compiler inserts it in. This is kind of a bug with Eclipse. The irony is annotation processors that ignore TYPE_USE do not have this problem but those that do can have issues. It is possible @stephan-herrmann has fixed this.
Anyway Google Auto Value is one of the few projects I know that does deal with TYPE_USE annotations correctly (and now this project will as well 😄 ) and they have issues with Eclipse (see):
I'm not saying you need to deal with it but just be aware of it.
Java's DAG for annotations processors doesn't contain
TYPE_USE
annotations on the Element for some reason. However, they are on the type. So, use the type instead.Note due to limitations of JavaPoet this doesn't fix
TYPE_USE
annotations on parameterized types or array components. If we want to address those we will need changes in JavaPoet which has been dormant for a very long time.Fixes #113 Relates to #111