From bcc8040afe9e06128df8f1ab11251e3b451e232a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sn=C5=8Dwball?= Date: Thu, 15 Oct 2020 22:57:32 +0200 Subject: [PATCH] Scalar type querybeans generics error (#2082) * Add failing test case for generic querybeans Types with generics arguments have not been processed correctly, they will lose their generics. This is not critical in Java, as the language has to be backwards compatible with Java 1.4, but will still become highlighted as a warning in all common IDEs. In Kotlin however, it's not a warning but a compile error. * Fix problem with generics in scalar type arg This fixes the issue introduced with ScalarType aware querybeans. --- ebean-querybean/pom.xml | 4 +- .../test/java/org/example/domain/Address.java | 9 +- .../domain/otherpackage/GenericType.java | 17 +++ .../otherpackage/GenericTypeArgument.java | 6 + .../GenericTypeAttributeConverter.java | 17 +++ kotlin-querybean-generator/pom.xml | 110 +++++++++++++++++- .../generator/ProcessingContext.java | 4 +- .../generator/PropertyTypeScalar.java | 27 +++-- .../PropertyTypeScalarComparable.java | 38 +----- .../io/ebean/querybean/generator/Split.java | 22 ++++ .../ebean/querybean/generator/SplitTest.java | 40 ------- .../io/ebean/querybean/generator/SplitTest.kt | 47 ++++++++ .../test/kotlin/org/example/domain/Address.kt | 24 ++++ .../kotlin/org/example/domain/BaseModel.kt | 26 +++++ .../kotlin/org/example/otherpackage/Email.kt | 3 + .../org/example/otherpackage/GenericType.kt | 5 + .../otherpackage/GenericTypeArgument.kt | 6 + .../GenericTypeAttributeConverter.kt | 15 +++ .../otherpackage/PhoneAttributeConverter.kt | 15 +++ .../org/example/otherpackage/PhoneNumber.kt | 5 + .../org/example/otherpackage/ValidEmail.kt | 9 ++ .../ValidEmailAttributeConverter.kt | 15 +++ .../generator/ProcessingContext.java | 4 +- .../generator/PropertyTypeScalar.java | 29 +++-- .../PropertyTypeScalarComparable.java | 36 +----- .../io/ebean/querybean/generator/Split.java | 23 ++++ .../ebean/querybean/generator/SplitTest.java | 14 ++- 27 files changed, 427 insertions(+), 143 deletions(-) create mode 100644 ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericType.java create mode 100644 ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericTypeArgument.java create mode 100644 ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericTypeAttributeConverter.java delete mode 100644 kotlin-querybean-generator/src/test/java/io/ebean/querybean/generator/SplitTest.java create mode 100644 kotlin-querybean-generator/src/test/kotlin/io/ebean/querybean/generator/SplitTest.kt create mode 100644 kotlin-querybean-generator/src/test/kotlin/org/example/domain/Address.kt create mode 100644 kotlin-querybean-generator/src/test/kotlin/org/example/domain/BaseModel.kt create mode 100644 kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/Email.kt create mode 100644 kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericType.kt create mode 100644 kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericTypeArgument.kt create mode 100644 kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericTypeAttributeConverter.kt create mode 100644 kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/PhoneAttributeConverter.kt create mode 100644 kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/PhoneNumber.kt create mode 100644 kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/ValidEmail.kt create mode 100644 kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/ValidEmailAttributeConverter.kt diff --git a/ebean-querybean/pom.xml b/ebean-querybean/pom.xml index 3db43bd24..805790c47 100644 --- a/ebean-querybean/pom.xml +++ b/ebean-querybean/pom.xml @@ -48,14 +48,14 @@ io.ebean ebean-ddlgen - 12.5.0 + ${project.version} test io.ebean querybean-generator - 12.5.0 + ${project.version} test diff --git a/ebean-querybean/src/test/java/org/example/domain/Address.java b/ebean-querybean/src/test/java/org/example/domain/Address.java index 7c46b1d90..f3b161dc8 100644 --- a/ebean-querybean/src/test/java/org/example/domain/Address.java +++ b/ebean-querybean/src/test/java/org/example/domain/Address.java @@ -5,6 +5,9 @@ import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.validation.constraints.Size; +import org.example.domain.otherpackage.GenericType; +import org.example.domain.otherpackage.GenericTypeArgument; + /** * Address entity bean. */ @@ -24,8 +27,10 @@ public class Address extends BaseModel { @ManyToOne Country country; + GenericType metadata; + /** - * Create a copy of the address. Used to provide a 'snapshot' of + * Create a copy of the address. Used to provide a 'snapshot' of * the shippingAddress for a give order. */ public Address createCopy() { @@ -36,7 +41,7 @@ public class Address extends BaseModel { copy.setCountry(country); return copy; } - + public String toString() { return id + " " + line1 + " " + line2 + " " + city + " " + country; } diff --git a/ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericType.java b/ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericType.java new file mode 100644 index 000000000..f2b97cd86 --- /dev/null +++ b/ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericType.java @@ -0,0 +1,17 @@ +package org.example.domain.otherpackage; + +public class GenericType { + private T data; + + public GenericType(final T data) { + this.data = data; + } + + public T getData() { + return data; + } + + public void setData(final T data) { + this.data = data; + } +} diff --git a/ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericTypeArgument.java b/ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericTypeArgument.java new file mode 100644 index 000000000..79ad89b6d --- /dev/null +++ b/ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericTypeArgument.java @@ -0,0 +1,6 @@ +package org.example.domain.otherpackage; + +public enum GenericTypeArgument { + FOO, + BAR; +} diff --git a/ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericTypeAttributeConverter.java b/ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericTypeAttributeConverter.java new file mode 100644 index 000000000..c5e574f62 --- /dev/null +++ b/ebean-querybean/src/test/java/org/example/domain/otherpackage/GenericTypeAttributeConverter.java @@ -0,0 +1,17 @@ +package org.example.domain.otherpackage; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter +public class GenericTypeAttributeConverter implements AttributeConverter, String> { + @Override + public String convertToDatabaseColumn(final GenericType attribute) { + return attribute.getData().name(); + } + + @Override + public GenericType convertToEntityAttribute(final String dbData) { + return new GenericType<>(GenericTypeArgument.valueOf(dbData)); + } +} diff --git a/kotlin-querybean-generator/pom.xml b/kotlin-querybean-generator/pom.xml index 2a04f2144..a2eceb8a5 100644 --- a/kotlin-querybean-generator/pom.xml +++ b/kotlin-querybean-generator/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 ebean-parent @@ -9,6 +10,10 @@ kotlin-querybean-generator + + 1.4.10 + + @@ -17,16 +22,100 @@ 3.1 test + + io.ebean + ebean-querybean + ${project.version} + + + io.ebean + ebean-core + ${project.version} + test + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + test + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + + + io.ebean + ebean-ddlgen + ${project.version} + test + + + javax.validation + validation-api + 1.1.0.Final + test + + src/test/kotlin + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + test-compile + test-compile + + test-compile + + + + test-kapt + + test-kapt + + + + src/test/kotlin + + + + io.ebean + kotlin-querybean-generator + ${project.version} + + + + + + + 1.8 + + org.apache.maven.plugins maven-compiler-plugin 3.2 + + + default-testCompile + none + + + java-test-compile + test-compile + + testCompile + + + 1.8 1.8 @@ -34,6 +123,25 @@ -proc:none + + + io.ebean + ebean-maven-plugin + 12.5.0 + + + test + process-test-classes + + debug=1 + + + testEnhance + + + + + diff --git a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java index 88242259b..f16bd5553 100644 --- a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java +++ b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java @@ -333,9 +333,9 @@ class ProcessingContext implements Constants { return result; } else { if (typeInstanceOf(typeMirror, "java.lang.Comparable")) { - return new PropertyTypeScalarComparable(typeDef(typeMirror)); + return new PropertyTypeScalarComparable(typeMirror.toString()); } else { - return new PropertyTypeScalar(typeDef(typeMirror)); + return new PropertyTypeScalar(typeMirror.toString()); } } } diff --git a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalar.java b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalar.java index 379ca1d7d..d92e1bbe0 100644 --- a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalar.java +++ b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalar.java @@ -1,17 +1,18 @@ package io.ebean.querybean.generator; +import java.util.Map.Entry; import java.util.Set; /** - * Property type for associated beans (OneToMany, ManyToOne etc). + * Property type for fields handled by ScalarTypes */ class PropertyTypeScalar extends PropertyType { /** * The package name for this associated query bean. */ - private final String assocPackage; - private final String attributeSimpleName; + private final Set assocImports; + private final String attributeCompleteSignature; /** * Construct given the associated bean type name and package. @@ -19,20 +20,26 @@ class PropertyTypeScalar extends PropertyType { * @param attributeClass the type in the database bean that will be serialized via ScalarType */ PropertyTypeScalar(String attributeClass) { - super("PScalar"); - int split = attributeClass.lastIndexOf('.'); - this.assocPackage = attributeClass.substring(0, split); - this.attributeSimpleName = attributeClass.substring(split + 1); + this("PScalar", attributeClass); + } + + protected PropertyTypeScalar(String propertyType, String attributeClass) { + super(propertyType); + + final Entry> signature = Split.genericsSplit(attributeClass); + + this.attributeCompleteSignature = signature.getKey(); + this.assocImports = signature.getValue(); } @Override String getTypeDefn(String shortName, boolean assoc) { if (assoc) { // PScalarType - return "PScalar"; + return propertyType + ""; } else { // PScalarType - return "PScalar"; + return propertyType + ""; } } @@ -42,7 +49,7 @@ class PropertyTypeScalar extends PropertyType { @Override void addImports(Set allImports) { super.addImports(allImports); - allImports.add(assocPackage + "." + attributeSimpleName); + allImports.addAll(assocImports); } } diff --git a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalarComparable.java b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalarComparable.java index 031b150a4..8acbcc919 100644 --- a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalarComparable.java +++ b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalarComparable.java @@ -1,17 +1,9 @@ package io.ebean.querybean.generator; -import java.util.Set; - /** - * Property type for associated beans (OneToMany, ManyToOne etc). + * Property type for fields handled by ScalarTypes */ -class PropertyTypeScalarComparable extends PropertyType { - - /** - * The package name for this associated query bean. - */ - private final String assocPackage; - private final String attributeSimpleName; +class PropertyTypeScalarComparable extends PropertyTypeScalar { /** * Construct given the associated bean type name and package. @@ -19,30 +11,6 @@ class PropertyTypeScalarComparable extends PropertyType { * @param attributeClass the type in the database bean that will be serialized via ScalarType */ PropertyTypeScalarComparable(String attributeClass) { - super("PScalarComparable"); - int split = attributeClass.lastIndexOf('.'); - this.assocPackage = attributeClass.substring(0, split); - this.attributeSimpleName = attributeClass.substring(split + 1); + super("PScalarComparable", attributeClass); } - - @Override - String getTypeDefn(String shortName, boolean assoc) { - if (assoc) { - // PScalarType - return "PScalarComparable"; - } else { - // PScalarType - return "PScalarComparable"; - } - } - - /** - * All required imports to the allImports set. - */ - @Override - void addImports(Set allImports) { - super.addImports(allImports); - allImports.add(assocPackage + "." + attributeSimpleName); - } - } diff --git a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Split.java b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Split.java index 61f6f2b55..0e6d10440 100644 --- a/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Split.java +++ b/kotlin-querybean-generator/src/main/java/io/ebean/querybean/generator/Split.java @@ -1,5 +1,11 @@ package io.ebean.querybean.generator; +import java.util.AbstractMap.SimpleEntry; +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Set; +import java.util.StringTokenizer; + /** * Helper for splitting package and class name. */ @@ -41,4 +47,20 @@ class Split { return fullType; } + static Entry> genericsSplit(String signature) { + StringBuilder simpleSignature = new StringBuilder(); + final StringTokenizer tokenizer = new StringTokenizer(signature, ",<> ", true); + + Set assocImports = new HashSet<>(); + while (tokenizer.hasMoreTokens()) { + final String token = tokenizer.nextToken(); + if (token.length() == 1 && ",<> ".indexOf(token.charAt(0)) >= 0) { + simpleSignature.append(token); + } else { + simpleSignature.append(Split.shortName(token)); + assocImports.add(token); + } + } + return new SimpleEntry<>(simpleSignature.toString(), assocImports); + } } diff --git a/kotlin-querybean-generator/src/test/java/io/ebean/querybean/generator/SplitTest.java b/kotlin-querybean-generator/src/test/java/io/ebean/querybean/generator/SplitTest.java deleted file mode 100644 index c382fd7b6..000000000 --- a/kotlin-querybean-generator/src/test/java/io/ebean/querybean/generator/SplitTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.ebean.querybean.generator; - -import org.testng.annotations.Test; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; - -public class SplitTest { - - @Test - public void trimType() { - assertEquals(Split.trimType("com.foo.domain.Customer"), "com.foo.domain.Customer"); - assertEquals(Split.trimType("? extends com.foo.domain.Customer"), "com.foo.domain.Customer"); - } - - @Test - public void shortName() { - assertEquals(Split.shortName("com.foo.domain.Customer"), "Customer"); - assertEquals(Split.shortName("Customer"), "Customer"); - } - - @Test - public void split_normal() { - - String[] split = Split.split("com.foo.domain.Customer"); - - assertEquals(split[0], "com.foo.domain"); - assertEquals(split[1], "Customer"); - } - - @Test - public void split_noPackage() { - - String[] split = Split.split("Customer"); - - assertNull(split[0]); - assertEquals(split[1], "Customer"); - } - -} \ No newline at end of file diff --git a/kotlin-querybean-generator/src/test/kotlin/io/ebean/querybean/generator/SplitTest.kt b/kotlin-querybean-generator/src/test/kotlin/io/ebean/querybean/generator/SplitTest.kt new file mode 100644 index 000000000..8c781f7d7 --- /dev/null +++ b/kotlin-querybean-generator/src/test/kotlin/io/ebean/querybean/generator/SplitTest.kt @@ -0,0 +1,47 @@ +package io.ebean.querybean.generator + +import org.testng.Assert.assertEquals +import org.testng.Assert.assertNull +import org.testng.annotations.Test +import java.lang.Exception +import java.util.Arrays +import java.util.HashSet + +class SplitTest { + @Test + fun trimType() { + assertEquals(Split.trimType("com.foo.domain.Customer"), "com.foo.domain.Customer") + assertEquals(Split.trimType("? extends com.foo.domain.Customer"), "com.foo.domain.Customer") + } + + @Test + fun shortName() { + assertEquals(Split.shortName("com.foo.domain.Customer"), "Customer") + assertEquals(Split.shortName("Customer"), "Customer") + } + + @Test + fun split_normal() { + val split = Split.split("com.foo.domain.Customer") + assertEquals(split[0], "com.foo.domain") + assertEquals(split[1], "Customer") + } + + @Test + fun split_noPackage() { + val split = Split.split("Customer") + assertNull(split[0]) + assertEquals(split[1], "Customer") + } + + @Test + @Throws(Exception::class) + fun split_generics() { + assertEquals("Foo>", Split.genericsSplit("com.Foo>").key) + assertEquals( + HashSet(Arrays.asList("com.Foo", "com.Bar", "org.XFoo", "org.XBar")), + Split.genericsSplit("com.Foo>").value + ) + assertEquals("Foo", Split.genericsSplit("com.bar.Foo").key) + } +} diff --git a/kotlin-querybean-generator/src/test/kotlin/org/example/domain/Address.kt b/kotlin-querybean-generator/src/test/kotlin/org/example/domain/Address.kt new file mode 100644 index 000000000..1669b6e0e --- /dev/null +++ b/kotlin-querybean-generator/src/test/kotlin/org/example/domain/Address.kt @@ -0,0 +1,24 @@ +package org.example.domain; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.Size; + +import org.example.otherpackage.GenericType; +import org.example.otherpackage.GenericTypeArgument; + +/** + * Address entity bean. + */ +@Entity +@Table(name = "o_address") +class Address( + @Size(max = 100) + var line1: String, + @Size(max = 100) + var line2: String, + @Size(max = 100) + var city: String, + // Dummy metadata field just to test generation + val metadata: GenericType +) : BaseModel() diff --git a/kotlin-querybean-generator/src/test/kotlin/org/example/domain/BaseModel.kt b/kotlin-querybean-generator/src/test/kotlin/org/example/domain/BaseModel.kt new file mode 100644 index 000000000..44789893f --- /dev/null +++ b/kotlin-querybean-generator/src/test/kotlin/org/example/domain/BaseModel.kt @@ -0,0 +1,26 @@ +package org.example.domain; + +import java.time.Instant; + +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import javax.persistence.Version; + +import io.ebean.Model; +import io.ebean.annotation.WhenCreated; +import io.ebean.annotation.WhenModified; + +@MappedSuperclass +abstract class BaseModel : Model() { + @Id + val id: Long? = null + + @Version + val version: Int = 0 + + @WhenCreated + val whenCreated: Instant? = null + + @WhenModified + val whenModified: Instant? = null +} diff --git a/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/Email.kt b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/Email.kt new file mode 100644 index 000000000..4845963d4 --- /dev/null +++ b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/Email.kt @@ -0,0 +1,3 @@ +package org.example.otherpackage + +interface Email : Comparable diff --git a/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericType.kt b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericType.kt new file mode 100644 index 000000000..6d03aeeab --- /dev/null +++ b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericType.kt @@ -0,0 +1,5 @@ +package org.example.otherpackage + +class GenericType( + val data: T +) diff --git a/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericTypeArgument.kt b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericTypeArgument.kt new file mode 100644 index 000000000..13e6dbce4 --- /dev/null +++ b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericTypeArgument.kt @@ -0,0 +1,6 @@ +package org.example.otherpackage + +enum class GenericTypeArgument { + FOO, + BAR, +} diff --git a/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericTypeAttributeConverter.kt b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericTypeAttributeConverter.kt new file mode 100644 index 000000000..a24154449 --- /dev/null +++ b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/GenericTypeAttributeConverter.kt @@ -0,0 +1,15 @@ +package org.example.otherpackage + +import javax.persistence.AttributeConverter +import javax.persistence.Converter + +@Converter +class GenericTypeAttributeConverter : AttributeConverter, String> { + override fun convertToDatabaseColumn(attribute: GenericType): String { + return attribute.data.name + } + + override fun convertToEntityAttribute(dbData: String): GenericType { + return GenericType(GenericTypeArgument.valueOf(dbData)) + } +} diff --git a/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/PhoneAttributeConverter.kt b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/PhoneAttributeConverter.kt new file mode 100644 index 000000000..55981a0a8 --- /dev/null +++ b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/PhoneAttributeConverter.kt @@ -0,0 +1,15 @@ +package org.example.otherpackage + +import javax.persistence.AttributeConverter +import javax.persistence.Converter + +@Converter +class PhoneAttributeConverter : AttributeConverter { + override fun convertToDatabaseColumn(attribute: PhoneNumber): String { + return attribute.msisdn + } + + override fun convertToEntityAttribute(dbData: String): PhoneNumber { + return PhoneNumber(dbData) + } +} diff --git a/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/PhoneNumber.kt b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/PhoneNumber.kt new file mode 100644 index 000000000..9c3dfef7c --- /dev/null +++ b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/PhoneNumber.kt @@ -0,0 +1,5 @@ +package org.example.otherpackage + +class PhoneNumber( + val msisdn: String +) diff --git a/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/ValidEmail.kt b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/ValidEmail.kt new file mode 100644 index 000000000..b0bdd55e1 --- /dev/null +++ b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/ValidEmail.kt @@ -0,0 +1,9 @@ +package org.example.otherpackage + +class ValidEmail( + val emailAddress: String +) : Email { + override fun compareTo(other: ValidEmail): Int { + return emailAddress.compareTo(other.emailAddress) + } +} diff --git a/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/ValidEmailAttributeConverter.kt b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/ValidEmailAttributeConverter.kt new file mode 100644 index 000000000..690978ce3 --- /dev/null +++ b/kotlin-querybean-generator/src/test/kotlin/org/example/otherpackage/ValidEmailAttributeConverter.kt @@ -0,0 +1,15 @@ +package org.example.otherpackage + +import javax.persistence.AttributeConverter +import javax.persistence.Converter + +@Converter +class ValidEmailAttributeConverter : AttributeConverter { + override fun convertToDatabaseColumn(attribute: ValidEmail): String { + return attribute.emailAddress + } + + override fun convertToEntityAttribute(dbData: String): ValidEmail { + return ValidEmail(dbData) + } +} diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java index 54cb79301..1000301f6 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/ProcessingContext.java @@ -298,9 +298,9 @@ class ProcessingContext implements Constants { return result; } else { if (typeInstanceOf(typeMirror, "java.lang.Comparable")) { - return new PropertyTypeScalarComparable(typeDef(typeMirror)); + return new PropertyTypeScalarComparable(typeMirror.toString()); } else { - return new PropertyTypeScalar(typeDef(typeMirror)); + return new PropertyTypeScalar(typeMirror.toString()); } } } diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalar.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalar.java index 379ca1d7d..dac7ea671 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalar.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalar.java @@ -1,17 +1,17 @@ package io.ebean.querybean.generator; +import java.util.Map.Entry; import java.util.Set; /** - * Property type for associated beans (OneToMany, ManyToOne etc). + * Property type for fields handled by ScalarTypes */ class PropertyTypeScalar extends PropertyType { - /** * The package name for this associated query bean. */ - private final String assocPackage; - private final String attributeSimpleName; + private final Set assocImports; + private final String attributeCompleteSignature; /** * Construct given the associated bean type name and package. @@ -19,20 +19,26 @@ class PropertyTypeScalar extends PropertyType { * @param attributeClass the type in the database bean that will be serialized via ScalarType */ PropertyTypeScalar(String attributeClass) { - super("PScalar"); - int split = attributeClass.lastIndexOf('.'); - this.assocPackage = attributeClass.substring(0, split); - this.attributeSimpleName = attributeClass.substring(split + 1); + this("PScalar", attributeClass); + } + + protected PropertyTypeScalar(String propertyType, String attributeClass) { + super(propertyType); + + final Entry> signature = Split.genericsSplit(attributeClass); + + this.attributeCompleteSignature = signature.getKey(); + this.assocImports = signature.getValue(); } @Override String getTypeDefn(String shortName, boolean assoc) { if (assoc) { // PScalarType - return "PScalar"; + return propertyType + ""; } else { // PScalarType - return "PScalar"; + return propertyType + ""; } } @@ -42,7 +48,6 @@ class PropertyTypeScalar extends PropertyType { @Override void addImports(Set allImports) { super.addImports(allImports); - allImports.add(assocPackage + "." + attributeSimpleName); + allImports.addAll(assocImports); } - } diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalarComparable.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalarComparable.java index 031b150a4..b91193395 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalarComparable.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/PropertyTypeScalarComparable.java @@ -1,17 +1,9 @@ package io.ebean.querybean.generator; -import java.util.Set; - /** * Property type for associated beans (OneToMany, ManyToOne etc). */ -class PropertyTypeScalarComparable extends PropertyType { - - /** - * The package name for this associated query bean. - */ - private final String assocPackage; - private final String attributeSimpleName; +class PropertyTypeScalarComparable extends PropertyTypeScalar { /** * Construct given the associated bean type name and package. @@ -19,30 +11,6 @@ class PropertyTypeScalarComparable extends PropertyType { * @param attributeClass the type in the database bean that will be serialized via ScalarType */ PropertyTypeScalarComparable(String attributeClass) { - super("PScalarComparable"); - int split = attributeClass.lastIndexOf('.'); - this.assocPackage = attributeClass.substring(0, split); - this.attributeSimpleName = attributeClass.substring(split + 1); + super("PScalarComparable", attributeClass); } - - @Override - String getTypeDefn(String shortName, boolean assoc) { - if (assoc) { - // PScalarType - return "PScalarComparable"; - } else { - // PScalarType - return "PScalarComparable"; - } - } - - /** - * All required imports to the allImports set. - */ - @Override - void addImports(Set allImports) { - super.addImports(allImports); - allImports.add(assocPackage + "." + attributeSimpleName); - } - } diff --git a/querybean-generator/src/main/java/io/ebean/querybean/generator/Split.java b/querybean-generator/src/main/java/io/ebean/querybean/generator/Split.java index c682344e7..70e8ad542 100644 --- a/querybean-generator/src/main/java/io/ebean/querybean/generator/Split.java +++ b/querybean-generator/src/main/java/io/ebean/querybean/generator/Split.java @@ -1,5 +1,11 @@ package io.ebean.querybean.generator; +import java.util.AbstractMap.SimpleEntry; +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Set; +import java.util.StringTokenizer; + /** * Helper for splitting package and class name. */ @@ -31,4 +37,21 @@ class Split { return className.substring(startPos + 1); } + static Entry> genericsSplit(String signature) { + StringBuilder simpleSignature = new StringBuilder(); + final StringTokenizer tokenizer = new StringTokenizer(signature, ",<> ", true); + + Set assocImports = new HashSet<>(); + while (tokenizer.hasMoreTokens()) { + final String token = tokenizer.nextToken(); + if (token.length() == 1 && ",<> ".indexOf(token.charAt(0)) >= 0) { + simpleSignature.append(token); + } else { + simpleSignature.append(Split.shortName(token)); + assocImports.add(token); + } + } + return new SimpleEntry<>(simpleSignature.toString(), assocImports); + } + } diff --git a/querybean-generator/src/test/java/io/ebean/querybean/generator/SplitTest.java b/querybean-generator/src/test/java/io/ebean/querybean/generator/SplitTest.java index 24be4a023..3ea80d6e3 100644 --- a/querybean-generator/src/test/java/io/ebean/querybean/generator/SplitTest.java +++ b/querybean-generator/src/test/java/io/ebean/querybean/generator/SplitTest.java @@ -1,10 +1,13 @@ package io.ebean.querybean.generator; -import org.testng.annotations.Test; - +import static java.util.Arrays.asList; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; +import java.util.HashSet; + +import org.testng.annotations.Test; + public class SplitTest { @Test @@ -32,4 +35,9 @@ public class SplitTest { assertEquals(split[1], "Customer"); } -} \ No newline at end of file + @Test + public void split_generics() throws Exception { + assertEquals("Foo>", Split.genericsSplit("com.Foo>").getKey()); + assertEquals(new HashSet<>(asList("com.Foo", "com.Bar", "org.XFoo", "org.XBar")), Split.genericsSplit("com.Foo>").getValue()); + } +}