From df09b11d7260cc4b196ebc7b465ed4a7f1f2ffb1 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Fri, 14 Mar 2025 17:31:27 -0400 Subject: [PATCH 1/4] Fix JDT records Fixes error where records generate incorrectly on JDT --- .../io/avaje/jsonb/generator/TypeReader.java | 55 +++++++++++++++---- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java index 3179c1e0..8ece5e24 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java @@ -3,6 +3,7 @@ import javax.lang.model.element.*; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; +import javax.lang.model.util.Elements; import static java.util.stream.Collectors.toSet; @@ -263,18 +264,48 @@ private void readMethod(Element element, List localFields) { } } // for getter/accessor methods only, not setters - PropertyPrism.getOptionalOn(methodElement).ifPresent(propertyPrism -> { - if (!methodElement.getParameters().isEmpty()) { - logError(errorContext + baseType + ", @Json.Property can only be placed on Getter Methods, but on %s", methodElement); - return; - } - - // getter property as simulated read-only field with getter method - final var frequency = frequency(propertyPrism.value()); - final var reader = new FieldReader(element, namingConvention, currentSubType, genericTypeParams, frequency); - reader.getterMethod(new MethodReader(methodElement)); - localFields.add(reader); - }); + PropertyPrism.getOptionalOn(methodElement) + .filter(p -> !hasRecordPropertyAnnotation(methodElement)) + .ifPresent( + propertyPrism -> { + if (!methodElement.getParameters().isEmpty()) { + logError( + errorContext + + baseType + + ", @Json.Property can only be placed on Getter Methods, but on %s", + methodElement); + return; + } + + // getter property as simulated read-only field with getter method + final var frequency = frequency(propertyPrism.value()); + final var reader = + new FieldReader( + element, namingConvention, currentSubType, genericTypeParams, frequency); + reader.getterMethod(new MethodReader(methodElement)); + localFields.add(reader); + }); + } + + private boolean hasRecordPropertyAnnotation(ExecutableElement methodElement) { + + try { + return APContext.jdkVersion() < 16 + || Optional.ofNullable( + Elements.class + .getMethod("recordComponentFor", ExecutableElement.class) + .invoke(APContext.elements(), methodElement)) + .map(Element.class::cast) + .flatMap( + e -> + ElementFilter.fieldsIn(e.getEnclosingElement().getEnclosedElements()).stream() + .filter(f -> f.getSimpleName().contentEquals(e.getSimpleName())) + .findAny()) + .filter(PropertyPrism::isPresent) + .isPresent(); + } catch (Exception e) { + return false; + } } private boolean checkMethod2(ExecutableElement methodElement) { From 86428192b933ec1910461e7450ccd9d42c2d0837 Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Fri, 14 Mar 2025 18:37:00 -0700 Subject: [PATCH 2/4] Update TypeReader.java --- .../src/main/java/io/avaje/jsonb/generator/TypeReader.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java index 8ece5e24..d3972db9 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java @@ -286,7 +286,7 @@ private void readMethod(Element element, List localFields) { localFields.add(reader); }); } - + private boolean hasRecordPropertyAnnotation(ExecutableElement methodElement) { try { @@ -297,7 +297,8 @@ private boolean hasRecordPropertyAnnotation(ExecutableElement methodElement) { .invoke(APContext.elements(), methodElement)) .map(Element.class::cast) .flatMap( - e -> + e -> // e is a RecordComponentElement that doesn't have the annotation + //so we look up the field by name to see if the annotation is present ElementFilter.fieldsIn(e.getEnclosingElement().getEnclosedElements()).stream() .filter(f -> f.getSimpleName().contentEquals(e.getSimpleName())) .findAny()) From cb72c099e3712236777c92fd09ce22f93f7ece8c Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Fri, 14 Mar 2025 18:42:12 -0700 Subject: [PATCH 3/4] Update TypeReader.java --- .../src/main/java/io/avaje/jsonb/generator/TypeReader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java index d3972db9..cb9798c0 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java @@ -290,8 +290,8 @@ private void readMethod(Element element, List localFields) { private boolean hasRecordPropertyAnnotation(ExecutableElement methodElement) { try { - return APContext.jdkVersion() < 16 - || Optional.ofNullable( + return APContext.jdkVersion() >= 16 + && Optional.ofNullable( Elements.class .getMethod("recordComponentFor", ExecutableElement.class) .invoke(APContext.elements(), methodElement)) From 70b4500ccd4e3c9e8e49a192d3db18a26413a198 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sat, 15 Mar 2025 16:41:31 +1300 Subject: [PATCH 4/4] Format and extract helper methods --- .../io/avaje/jsonb/generator/TypeReader.java | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java index cb9798c0..ec4246bf 100644 --- a/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java +++ b/jsonb-generator/src/main/java/io/avaje/jsonb/generator/TypeReader.java @@ -265,50 +265,50 @@ private void readMethod(Element element, List localFields) { } // for getter/accessor methods only, not setters PropertyPrism.getOptionalOn(methodElement) - .filter(p -> !hasRecordPropertyAnnotation(methodElement)) - .ifPresent( - propertyPrism -> { - if (!methodElement.getParameters().isEmpty()) { - logError( - errorContext - + baseType - + ", @Json.Property can only be placed on Getter Methods, but on %s", - methodElement); - return; - } - - // getter property as simulated read-only field with getter method - final var frequency = frequency(propertyPrism.value()); - final var reader = - new FieldReader( - element, namingConvention, currentSubType, genericTypeParams, frequency); - reader.getterMethod(new MethodReader(methodElement)); - localFields.add(reader); - }); - } - - private boolean hasRecordPropertyAnnotation(ExecutableElement methodElement) { + .filter(p -> !hasRecordPropertyAnnotation(methodElement)) + .ifPresent(propertyPrism -> { + if (!methodElement.getParameters().isEmpty()) { + logError(errorContext + baseType + ", @Json.Property can only be placed on Getter Methods, but on %s", methodElement); + return; + } + + // getter property as simulated read-only field with getter method + final var frequency = frequency(propertyPrism.value()); + final var reader = new FieldReader(element, namingConvention, currentSubType, genericTypeParams, frequency); + reader.getterMethod(new MethodReader(methodElement)); + localFields.add(reader); + }); + } + private boolean hasRecordPropertyAnnotation(ExecutableElement methodElement) { try { return APContext.jdkVersion() >= 16 - && Optional.ofNullable( - Elements.class - .getMethod("recordComponentFor", ExecutableElement.class) - .invoke(APContext.elements(), methodElement)) - .map(Element.class::cast) - .flatMap( - e -> // e is a RecordComponentElement that doesn't have the annotation - //so we look up the field by name to see if the annotation is present - ElementFilter.fieldsIn(e.getEnclosingElement().getEnclosedElements()).stream() - .filter(f -> f.getSimpleName().contentEquals(e.getSimpleName())) - .findAny()) - .filter(PropertyPrism::isPresent) - .isPresent(); + && Optional.ofNullable(recordComponentFor(methodElement)) + .map(Element.class::cast) + .flatMap(TypeReader::matchingField) + .filter(PropertyPrism::isPresent) + .isPresent(); } catch (Exception e) { return false; } } + /** + * e is a RecordComponentElement that doesn't have the annotation + * look up the field by name to see if the annotation is present + */ + private static Optional matchingField(Element e) { + return ElementFilter.fieldsIn(e.getEnclosingElement().getEnclosedElements()).stream() + .filter(f -> f.getSimpleName().contentEquals(e.getSimpleName())) + .findAny(); + } + + private static Object recordComponentFor(ExecutableElement methodElement) throws Exception { + return Elements.class + .getMethod("recordComponentFor", ExecutableElement.class) + .invoke(APContext.elements(), methodElement); + } + private boolean checkMethod2(ExecutableElement methodElement) { if (!Util.isPublic(methodElement)) { return false;