From cdaf7bf24664912f106ad135eda2945b0e12f2c4 Mon Sep 17 00:00:00 2001 From: michi Date: Sun, 16 Jan 2022 11:57:27 +0100 Subject: [PATCH] Support @GeneratePojobuilder on records --- .../pojobuilder/analysis/InputFactory.java | 2 +- .../analysis/JavaModelAnalyzer.java | 3 +- .../karneim/pojobuilder/testenv/TestBase.java | 1 + .../AnnotationProcessor_Samples_Test.java | 84 +++++++++++++++ .../resources/samples/BarBuilder.java | 74 +++++++++++++ .../resources/samples/FooBuilder.java | 74 +++++++++++++ .../resources/samples/Record.java | 6 ++ .../resources/samples/RecordBuilder.java | 102 ++++++++++++++++++ .../samples/RecordsInUmbrellaClass.java | 13 +++ src/testdata/java/samples/Record.java.text | 6 ++ .../java/samples/RecordBuilder.java.text | 102 ++++++++++++++++++ 11 files changed, 465 insertions(+), 2 deletions(-) create mode 100644 src/testdata-java16/resources/samples/BarBuilder.java create mode 100644 src/testdata-java16/resources/samples/FooBuilder.java create mode 100644 src/testdata-java16/resources/samples/Record.java create mode 100644 src/testdata-java16/resources/samples/RecordBuilder.java create mode 100644 src/testdata-java16/resources/samples/RecordsInUmbrellaClass.java create mode 100644 src/testdata/java/samples/Record.java.text create mode 100644 src/testdata/java/samples/RecordBuilder.java.text diff --git a/src/main/java/net/karneim/pojobuilder/analysis/InputFactory.java b/src/main/java/net/karneim/pojobuilder/analysis/InputFactory.java index 08bf7f3..5ac10ed 100644 --- a/src/main/java/net/karneim/pojobuilder/analysis/InputFactory.java +++ b/src/main/java/net/karneim/pojobuilder/analysis/InputFactory.java @@ -24,7 +24,7 @@ public InputFactory(Types types, DirectivesFactory directivesFactory) { } public Input getInput(Element annotatedElement) { - if (annotatedElement.getKind() == ElementKind.CLASS) { + if (annotatedElement.getKind() == ElementKind.CLASS || "RECORD".equals(annotatedElement.getKind().name())) { TypeElement typeEl = (TypeElement) annotatedElement; if (typeEl.getModifiers().contains(Modifier.PRIVATE)) { throw new InvalidElementException(String.format("Pojo %s must not be private!", annotatedElement), diff --git a/src/main/java/net/karneim/pojobuilder/analysis/JavaModelAnalyzer.java b/src/main/java/net/karneim/pojobuilder/analysis/JavaModelAnalyzer.java index 9a4eea1..56c741a 100755 --- a/src/main/java/net/karneim/pojobuilder/analysis/JavaModelAnalyzer.java +++ b/src/main/java/net/karneim/pojobuilder/analysis/JavaModelAnalyzer.java @@ -305,7 +305,8 @@ private void scanSourceCode(Input input, Output result) { pojoConstructorScanner.scan((ExecutableElement) input.getAnnotatedElement(), result); } else if (input.getAnnotatedElement().getKind() == ElementKind.METHOD) { factoryMethodScanner.scan((ExecutableElement) input.getAnnotatedElement(), result); - } else if (input.getAnnotatedElement().getKind() == ElementKind.CLASS) { + } else if (input.getAnnotatedElement().getKind() == ElementKind.CLASS + || "RECORD".equals(input.getAnnotatedElement().getKind().name())) { TypeElement cls = (TypeElement) input.getAnnotatedElement(); pojoConstructorScanner.scan(cls, result); } diff --git a/src/test/java/net/karneim/pojobuilder/testenv/TestBase.java b/src/test/java/net/karneim/pojobuilder/testenv/TestBase.java index 9add732..586997e 100644 --- a/src/test/java/net/karneim/pojobuilder/testenv/TestBase.java +++ b/src/test/java/net/karneim/pojobuilder/testenv/TestBase.java @@ -6,6 +6,7 @@ public abstract class TestBase { public static final String TESTDATA_DIRECTORY = "src/testdata/java"; + public static final String TESTDATA_DIRECTORY_JAVA16 = "src/testdata-java16/resources"; private static final boolean DEBUG_LOG_ENABLED = false; diff --git a/src/test/java/samples/AnnotationProcessor_Samples_Test.java b/src/test/java/samples/AnnotationProcessor_Samples_Test.java index d771c88..5c66f93 100644 --- a/src/test/java/samples/AnnotationProcessor_Samples_Test.java +++ b/src/test/java/samples/AnnotationProcessor_Samples_Test.java @@ -1,9 +1,13 @@ package samples; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assume.assumeTrue; import java.io.File; import java.net.URL; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.lang.model.SourceVersion; import net.karneim.pojobuilder.processor.AnnotationProcessor; import net.karneim.pojobuilder.processor.with.ProcessorTestSupport; @@ -345,4 +349,84 @@ public void testShouldGenerateGregorianCalendarBuilder() throws Exception { assertThat(actual).isEqualTo(expected); assertThat(prj.findClass(builderClassname)).isNotNull(); } + + + private void assumeJdkSupportsAtLeastSourceVersion(int minimumSourceVersion) { + String latestSourceVersion = SourceVersion.latest().name(); + Matcher m = Pattern.compile("^RELEASE_(\\d+)$").matcher(latestSourceVersion); + if (m.find()) { + int sourceVersion = Integer.valueOf(m.group(1)); + if (sourceVersion >= minimumSourceVersion) { + return; + } + } + String jreVersion = System.getProperty("java.version"); + String msg = "JRE " + jreVersion + " does not support records"; + // better than throwing a Junit4 internal exception + assumeTrue(msg, false); + } + + /** + * @scenario Generating a builder for a record. + *

+ * This test will be skipped if the current JRE does not support RELEASE_16 + * as source version. + * @throws Exception + */ + @Test + public void testShouldGenerateRecordBuilder() throws Exception { + assumeJdkSupportsAtLeastSourceVersion(16); + + // Given: + String pojoClassname = "samples.Record"; + String builderClassname = "samples.RecordBuilder"; + prj.addSourceFile(getSourceFilename(TESTDATA_DIRECTORY_JAVA16, pojoClassname)); + + // When: + boolean success = prj.compile(); + + // Then: + assertThat(success).isTrue(); + String actual = getContent(prj.findGeneratedSource(builderClassname)); + logDebug(actual); + + String expected = loadJavaSourceFromFilesystem(TESTDATA_DIRECTORY_JAVA16, builderClassname); + assertThat(actual).isEqualTo(expected); + assertThat(prj.findClass(builderClassname)).isNotNull(); + } + + /** + * @scenario Generating a builder for a record. + *

+ * This test will be skipped if the current JRE does not support RELEASE_16 + * as source version. + * @throws Exception + */ + @Test + public void testShouldGenerateRecordsInUmbrellaClass() throws Exception { + assumeJdkSupportsAtLeastSourceVersion(16); + + // Given: + String umbrellaClassname = "samples.RecordsInUmbrellaClass"; + String fooBuilderClassname = "samples.FooBuilder"; + String barBuilderClassname = "samples.BarBuilder"; + prj.addSourceFile(getSourceFilename(TESTDATA_DIRECTORY_JAVA16, umbrellaClassname)); + + // When: + boolean success = prj.compile(); + + // Then: + assertThat(success).isTrue(); + String actualFooBuilder = getContent(prj.findGeneratedSource(fooBuilderClassname)); + logDebug(actualFooBuilder); + String expectedFooBuilder = loadJavaSourceFromFilesystem(TESTDATA_DIRECTORY_JAVA16, fooBuilderClassname); + assertThat(actualFooBuilder).isEqualTo(expectedFooBuilder); + assertThat(prj.findClass(fooBuilderClassname)).isNotNull(); + + String actualBarBuilder = getContent(prj.findGeneratedSource(barBuilderClassname)); + logDebug(actualBarBuilder); + String expectedBarBuilder = loadJavaSourceFromFilesystem(TESTDATA_DIRECTORY_JAVA16, barBuilderClassname); + assertThat(actualBarBuilder).isEqualTo(expectedBarBuilder); + assertThat(prj.findClass(barBuilderClassname)).isNotNull(); + } } diff --git a/src/testdata-java16/resources/samples/BarBuilder.java b/src/testdata-java16/resources/samples/BarBuilder.java new file mode 100644 index 0000000..fd8fd56 --- /dev/null +++ b/src/testdata-java16/resources/samples/BarBuilder.java @@ -0,0 +1,74 @@ +package samples; + +import javax.annotation.processing.Generated; +import net.karneim.pojobuilder.GwtIncompatible; + +@Generated("PojoBuilder") +public class BarBuilder + implements Cloneable { + protected BarBuilder self; + protected RecordsInUmbrellaClass.Foo value$foo$samples$RecordsInUmbrellaClass$Foo; + protected boolean isSet$foo$samples$RecordsInUmbrellaClass$Foo; + + /** + * Creates a new {@link BarBuilder}. + */ + public BarBuilder() { + self = (BarBuilder)this; + } + + /** + * Sets the default value for the foo property. + * + * @param value the default value + * @return this builder + */ + public BarBuilder withFoo(RecordsInUmbrellaClass.Foo value) { + this.value$foo$samples$RecordsInUmbrellaClass$Foo = value; + this.isSet$foo$samples$RecordsInUmbrellaClass$Foo = true; + return self; + } + + /** + * Returns a clone of this builder. + * + * @return the clone + */ + @Override + @GwtIncompatible + public Object clone() { + try { + BarBuilder result = (BarBuilder)super.clone(); + result.self = result; + return result; + } catch (CloneNotSupportedException e) { + throw new InternalError(e.getMessage()); + } + } + + /** + * Returns a clone of this builder. + * + * @return the clone + */ + @GwtIncompatible + public BarBuilder but() { + return (BarBuilder)clone(); + } + + /** + * Creates a new {@link RecordsInUmbrellaClass.Bar} based on this builder's settings. + * + * @return the created RecordsInUmbrellaClass.Bar + */ + public RecordsInUmbrellaClass.Bar build() { + try { + RecordsInUmbrellaClass.Bar result = new RecordsInUmbrellaClass.Bar(value$foo$samples$RecordsInUmbrellaClass$Foo); + return result; + } catch (RuntimeException ex) { + throw ex; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/src/testdata-java16/resources/samples/FooBuilder.java b/src/testdata-java16/resources/samples/FooBuilder.java new file mode 100644 index 0000000..10ff3c7 --- /dev/null +++ b/src/testdata-java16/resources/samples/FooBuilder.java @@ -0,0 +1,74 @@ +package samples; + +import javax.annotation.processing.Generated; +import net.karneim.pojobuilder.GwtIncompatible; + +@Generated("PojoBuilder") +public class FooBuilder + implements Cloneable { + protected FooBuilder self; + protected int value$foo$int; + protected boolean isSet$foo$int; + + /** + * Creates a new {@link FooBuilder}. + */ + public FooBuilder() { + self = (FooBuilder)this; + } + + /** + * Sets the default value for the foo property. + * + * @param value the default value + * @return this builder + */ + public FooBuilder withFoo(int value) { + this.value$foo$int = value; + this.isSet$foo$int = true; + return self; + } + + /** + * Returns a clone of this builder. + * + * @return the clone + */ + @Override + @GwtIncompatible + public Object clone() { + try { + FooBuilder result = (FooBuilder)super.clone(); + result.self = result; + return result; + } catch (CloneNotSupportedException e) { + throw new InternalError(e.getMessage()); + } + } + + /** + * Returns a clone of this builder. + * + * @return the clone + */ + @GwtIncompatible + public FooBuilder but() { + return (FooBuilder)clone(); + } + + /** + * Creates a new {@link RecordsInUmbrellaClass.Foo} based on this builder's settings. + * + * @return the created RecordsInUmbrellaClass.Foo + */ + public RecordsInUmbrellaClass.Foo build() { + try { + RecordsInUmbrellaClass.Foo result = new RecordsInUmbrellaClass.Foo(value$foo$int); + return result; + } catch (RuntimeException ex) { + throw ex; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/src/testdata-java16/resources/samples/Record.java b/src/testdata-java16/resources/samples/Record.java new file mode 100644 index 0000000..9950545 --- /dev/null +++ b/src/testdata-java16/resources/samples/Record.java @@ -0,0 +1,6 @@ +package samples; + +import net.karneim.pojobuilder.GeneratePojoBuilder; + +@GeneratePojoBuilder +public record Record(String name, User assignee, String description) {} diff --git a/src/testdata-java16/resources/samples/RecordBuilder.java b/src/testdata-java16/resources/samples/RecordBuilder.java new file mode 100644 index 0000000..9f512d6 --- /dev/null +++ b/src/testdata-java16/resources/samples/RecordBuilder.java @@ -0,0 +1,102 @@ +package samples; + +import javax.annotation.processing.Generated; +import net.karneim.pojobuilder.GwtIncompatible; + +@Generated("PojoBuilder") +public class RecordBuilder + implements Cloneable { + protected RecordBuilder self; + protected String value$name$java$lang$String; + protected boolean isSet$name$java$lang$String; + protected User value$assignee$samples$User; + protected boolean isSet$assignee$samples$User; + protected String value$description$java$lang$String; + protected boolean isSet$description$java$lang$String; + + /** + * Creates a new {@link RecordBuilder}. + */ + public RecordBuilder() { + self = (RecordBuilder)this; + } + + /** + * Sets the default value for the name property. + * + * @param value the default value + * @return this builder + */ + public RecordBuilder withName(String value) { + this.value$name$java$lang$String = value; + this.isSet$name$java$lang$String = true; + return self; + } + + /** + * Sets the default value for the assignee property. + * + * @param value the default value + * @return this builder + */ + public RecordBuilder withAssignee(User value) { + this.value$assignee$samples$User = value; + this.isSet$assignee$samples$User = true; + return self; + } + + /** + * Sets the default value for the description property. + * + * @param value the default value + * @return this builder + */ + public RecordBuilder withDescription(String value) { + this.value$description$java$lang$String = value; + this.isSet$description$java$lang$String = true; + return self; + } + + /** + * Returns a clone of this builder. + * + * @return the clone + */ + @Override + @GwtIncompatible + public Object clone() { + try { + RecordBuilder result = (RecordBuilder)super.clone(); + result.self = result; + return result; + } catch (CloneNotSupportedException e) { + throw new InternalError(e.getMessage()); + } + } + + /** + * Returns a clone of this builder. + * + * @return the clone + */ + @GwtIncompatible + public RecordBuilder but() { + return (RecordBuilder)clone(); + } + + /** + * Creates a new {@link Record} based on this builder's settings. + * + * @return the created Record + */ + public Record build() { + try { + Record result = new Record(value$name$java$lang$String, value$assignee$samples$User, value$description$java$lang$String); + return result; + } catch (RuntimeException ex) { + throw ex; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/src/testdata-java16/resources/samples/RecordsInUmbrellaClass.java b/src/testdata-java16/resources/samples/RecordsInUmbrellaClass.java new file mode 100644 index 0000000..0464005 --- /dev/null +++ b/src/testdata-java16/resources/samples/RecordsInUmbrellaClass.java @@ -0,0 +1,13 @@ +package samples; + +import net.karneim.pojobuilder.GeneratePojoBuilder; + +public class RecordsInUmbrellaClass { + @GeneratePojoBuilder + public record Foo(int foo) {}; + + @GeneratePojoBuilder + public record Bar(Foo foo) {}; + + public record Ignore(int ignore) {}; +} \ No newline at end of file diff --git a/src/testdata/java/samples/Record.java.text b/src/testdata/java/samples/Record.java.text new file mode 100644 index 0000000..9950545 --- /dev/null +++ b/src/testdata/java/samples/Record.java.text @@ -0,0 +1,6 @@ +package samples; + +import net.karneim.pojobuilder.GeneratePojoBuilder; + +@GeneratePojoBuilder +public record Record(String name, User assignee, String description) {} diff --git a/src/testdata/java/samples/RecordBuilder.java.text b/src/testdata/java/samples/RecordBuilder.java.text new file mode 100644 index 0000000..9f512d6 --- /dev/null +++ b/src/testdata/java/samples/RecordBuilder.java.text @@ -0,0 +1,102 @@ +package samples; + +import javax.annotation.processing.Generated; +import net.karneim.pojobuilder.GwtIncompatible; + +@Generated("PojoBuilder") +public class RecordBuilder + implements Cloneable { + protected RecordBuilder self; + protected String value$name$java$lang$String; + protected boolean isSet$name$java$lang$String; + protected User value$assignee$samples$User; + protected boolean isSet$assignee$samples$User; + protected String value$description$java$lang$String; + protected boolean isSet$description$java$lang$String; + + /** + * Creates a new {@link RecordBuilder}. + */ + public RecordBuilder() { + self = (RecordBuilder)this; + } + + /** + * Sets the default value for the name property. + * + * @param value the default value + * @return this builder + */ + public RecordBuilder withName(String value) { + this.value$name$java$lang$String = value; + this.isSet$name$java$lang$String = true; + return self; + } + + /** + * Sets the default value for the assignee property. + * + * @param value the default value + * @return this builder + */ + public RecordBuilder withAssignee(User value) { + this.value$assignee$samples$User = value; + this.isSet$assignee$samples$User = true; + return self; + } + + /** + * Sets the default value for the description property. + * + * @param value the default value + * @return this builder + */ + public RecordBuilder withDescription(String value) { + this.value$description$java$lang$String = value; + this.isSet$description$java$lang$String = true; + return self; + } + + /** + * Returns a clone of this builder. + * + * @return the clone + */ + @Override + @GwtIncompatible + public Object clone() { + try { + RecordBuilder result = (RecordBuilder)super.clone(); + result.self = result; + return result; + } catch (CloneNotSupportedException e) { + throw new InternalError(e.getMessage()); + } + } + + /** + * Returns a clone of this builder. + * + * @return the clone + */ + @GwtIncompatible + public RecordBuilder but() { + return (RecordBuilder)clone(); + } + + /** + * Creates a new {@link Record} based on this builder's settings. + * + * @return the created Record + */ + public Record build() { + try { + Record result = new Record(value$name$java$lang$String, value$assignee$samples$User, value$description$java$lang$String); + return result; + } catch (RuntimeException ex) { + throw ex; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } +}