From 1a6924c2977481fa5f0bf7c8d975bfdd33988aba Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 28 Jul 2021 11:55:27 +0200 Subject: [PATCH] HHH-14724 Fix generation of problematic metamodel classes which use TYPE_USE annotations --- .../hibernate-jpamodelgen.gradle | 2 +- .../util/TypeRenderingVisitor.java | 182 ++++++++++++++++++ .../hibernate/jpamodelgen/util/TypeUtils.java | 2 +- 3 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeRenderingVisitor.java diff --git a/tooling/metamodel-generator/hibernate-jpamodelgen.gradle b/tooling/metamodel-generator/hibernate-jpamodelgen.gradle index e6b7fde363dc..9f3330401cdc 100644 --- a/tooling/metamodel-generator/hibernate-jpamodelgen.gradle +++ b/tooling/metamodel-generator/hibernate-jpamodelgen.gradle @@ -30,7 +30,7 @@ dependencies { testCompile libraries.junit testCompile libraries.jpa - testCompile libraries.validation + testCompile libraries.validation testCompile project( ':hibernate-core' ) } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeRenderingVisitor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeRenderingVisitor.java new file mode 100644 index 000000000000..5652b3799161 --- /dev/null +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeRenderingVisitor.java @@ -0,0 +1,182 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.jpamodelgen.util; + +import java.util.List; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.IntersectionType; +import javax.lang.model.type.NoType; +import javax.lang.model.type.NullType; +import javax.lang.model.type.PrimitiveType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.UnionType; +import javax.lang.model.type.WildcardType; +import javax.lang.model.util.SimpleTypeVisitor8; + +/** + * @author Christian Beikov + */ +public final class TypeRenderingVisitor extends SimpleTypeVisitor8 { + + private final StringBuilder sb = new StringBuilder(); + + private TypeRenderingVisitor() { + } + + public static String toString(TypeMirror typeMirror) { + if ( typeMirror instanceof TypeVariable ) { + // Top level type variables don't need to render the upper bound as `T extends Type` + final Element typeVariableElement = ( (TypeVariable) typeMirror ).asElement(); + if ( typeVariableElement instanceof TypeParameterElement ) { + final TypeParameterElement typeParameter = (TypeParameterElement) typeVariableElement; + if ( typeParameter.getEnclosingElement().getKind() == ElementKind.METHOD ) { + // But for method level type variable we return the upper bound + // because the type variable has no meaning except for that method + typeMirror = ( (TypeVariable) typeMirror ).getUpperBound(); + } + else { + return typeParameter.toString(); + } + } + else { + typeMirror = typeVariableElement.asType(); + } + } + else if ( typeMirror instanceof IntersectionType ) { + // For top level type only the first type is relevant + typeMirror = ( (IntersectionType) typeMirror ).getBounds().get( 0 ); + } + final TypeRenderingVisitor typeRenderingVisitor = new TypeRenderingVisitor(); + typeMirror.accept( typeRenderingVisitor, null ); + return typeRenderingVisitor.sb.toString(); + } + + @Override + public Object visitPrimitive(PrimitiveType t, Object o) { + final String primitiveTypeName = getPrimitiveTypeName( t.getKind() ); + if ( primitiveTypeName != null ) { + sb.append( primitiveTypeName ); + } + return null; + } + + private static String getPrimitiveTypeName(TypeKind kind) { + switch ( kind ) { + case INT: + return "int"; + case BOOLEAN: + return "boolean"; + case BYTE: + return "byte"; + case CHAR: + return "char"; + case DOUBLE: + return "double"; + case FLOAT: + return "float"; + case LONG: + return "long"; + case SHORT: + return "short"; + case VOID: + return "void"; + } + return null; + } + + @Override + public Object visitNull(NullType t, Object o) { + return null; + } + + @Override + public Object visitArray(ArrayType t, Object o) { + t.getComponentType().accept( this, null ); + sb.append( "[]" ); + return t; + } + + @Override + public Object visitDeclared(DeclaredType t, Object o) { + sb.append( t.asElement().toString() ); + List typeArguments = t.getTypeArguments(); + if ( !typeArguments.isEmpty() ) { + sb.append( '<' ); + typeArguments.get( 0 ).accept( this, null ); + for ( int i = 1; i < typeArguments.size(); i++ ) { + sb.append( ", " ); + typeArguments.get( i ).accept( this, null ); + } + sb.append( '>' ); + } + return null; + } + + @Override + public Object visitTypeVariable(TypeVariable t, Object o) { + final Element typeVariableElement = t.asElement(); + if ( typeVariableElement instanceof TypeParameterElement ) { + final TypeParameterElement typeParameter = (TypeParameterElement) typeVariableElement; + sb.append( typeParameter ); + if ( !"java.lang.Object".equals( t.getUpperBound().toString() ) ) { + sb.append( " extends " ); + t.getUpperBound().accept( this, null ); + } + } + else { + typeVariableElement.asType().accept( this, null ); + } + return null; + } + + @Override + public Object visitWildcard(WildcardType t, Object o) { + sb.append( '?' ); + if ( t.getExtendsBound() != null ) { + sb.append( " extends " ); + t.getExtendsBound().accept( this, null ); + } + if ( t.getSuperBound() != null ) { + sb.append( " super " ); + t.getSuperBound().accept( this, null ); + } + return null; + } + + @Override + public Object visitUnion(UnionType t, Object o) { + return null; + } + + @Override + public Object visitIntersection(IntersectionType t, Object o) { + final List bounds = t.getBounds(); + bounds.get( 0 ).accept( this, null ); + for ( int i = 0; i < bounds.size(); i++ ) { + sb.append( " & " ); + bounds.get( i ).accept( this, null ); + } + return null; + } + + @Override + public Object visitExecutable(ExecutableType t, Object o) { + return null; + } + + @Override + public Object visitNoType(NoType t, Object o) { + return null; + } +} diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java index 5c7610d4f3aa..ea5b1ac888b9 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java @@ -74,7 +74,7 @@ public static String toTypeString(TypeMirror type) { if ( type.getKind().isPrimitive() ) { return PRIMITIVE_WRAPPERS.get( type.getKind() ); } - return type.toString(); + return TypeRenderingVisitor.toString( type ); } public static String toArrayTypeString(ArrayType type, Context context) {