HHH-18693 Changed code to allow creation of metadata for inner static non-private classes

Generaed metadate class for inner class A.B is A_.B_
This commit is contained in:
Čedomir Igaly 2024-11-22 19:17:00 +01:00 committed by Gavin King
parent d91d87c516
commit d3a1ebd0e2
15 changed files with 415 additions and 121 deletions

View File

@ -4,11 +4,16 @@
*/
package org.hibernate.processor;
import org.hibernate.processor.annotation.InnerClassMetaAttribute;
import org.hibernate.processor.model.MetaAttribute;
import org.hibernate.processor.model.Metamodel;
import org.hibernate.processor.util.StringUtil;
import javax.annotation.processing.FilerException;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
@ -18,7 +23,12 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.hibernate.processor.util.TypeUtils.isMemberType;
/**
* Helper class to write the actual metamodel class using the {@link javax.annotation.processing.Filer} API.
@ -39,7 +49,7 @@ public final class ClassWriter {
String body = generateBody( entity, context ).toString();
FileObject fo = context.getProcessingEnvironment().getFiler().createSourceFile(
getFullyQualifiedClassName( entity, metaModelPackage ),
getFullyQualifiedClassName( entity ),
entity.getElement()
);
OutputStream os = fo.openOutputStream();
@ -101,6 +111,15 @@ public final class ClassWriter {
pw.println();
final List<MetaAttribute> members = entity.getMembers();
for ( MetaAttribute metaMember : members ) {
if ( metaMember instanceof InnerClassMetaAttribute innerClass ) {
generateBody( innerClass.getMetaEntity(), context )
.toString().lines()
.forEach(line -> pw.println('\t' + line));
context.markGenerated( innerClass.getMetaEntity() );
}
}
for ( MetaAttribute metaMember : members ) {
if ( metaMember.hasStringAttribute() ) {
metaMember.getAttributeNameDeclarationString().lines()
@ -133,16 +152,29 @@ public final class ClassWriter {
}
private static void printClassDeclaration(Metamodel entity, PrintWriter pw) {
if ( isMemberType( entity.getElement() ) ) {
final Set<Modifier> modifiers = entity.getElement().getModifiers();
if ( modifiers.contains( Modifier.PUBLIC ) ) {
pw.print( "public " );
}
else if ( modifiers.contains( Modifier.PROTECTED ) ) {
pw.print( "protected " );
}
pw.print( "static " );
}
else {
pw.print( "public " );
}
if ( !entity.isImplementation() && !entity.isJakartaDataStyle() ) {
pw.print( "abstract " );
}
pw.print( entity.isJakartaDataStyle() ? "interface " : "class " );
pw.print( getGeneratedClassName(entity) );
String superClassName = entity.getSupertypeName();
if ( superClassName != null ) {
pw.print( " extends " + getGeneratedSuperclassName(entity, superClassName) );
final Element superTypeElement = entity.getSuperTypeElement();
if ( superTypeElement != null ) {
pw.print( " extends " +
entity.importType(getGeneratedSuperclassName( superTypeElement, entity.isJakartaDataStyle() )) );
}
if ( entity.isImplementation() ) {
pw.print( entity.getElement().getKind() == ElementKind.CLASS ? " extends " : " implements " );
@ -152,13 +184,21 @@ public final class ClassWriter {
pw.println( " {" );
}
private static String getFullyQualifiedClassName(Metamodel entity, String metaModelPackage) {
String fullyQualifiedClassName = "";
if ( !metaModelPackage.isEmpty() ) {
fullyQualifiedClassName = fullyQualifiedClassName + metaModelPackage + ".";
private static String getFullyQualifiedClassName(Metamodel entity) {
final String metaModelPackage = entity.getPackageName();
final String packageNamePrefix = !metaModelPackage.isEmpty() ? metaModelPackage + "." : "";
final String className;
if ( entity.getElement().getKind() == ElementKind.PACKAGE ) {
className = getGeneratedClassName( entity );
}
fullyQualifiedClassName = fullyQualifiedClassName + getGeneratedClassName( entity );
return fullyQualifiedClassName;
else {
className = Arrays.stream(
entity.getQualifiedName().substring( packageNamePrefix.length() ).split( "\\." ) )
.map( StringUtil::removeDollar )
.map( part -> entity.isJakartaDataStyle() ? '_' + part : part + '_' )
.collect( Collectors.joining( "." ) );
}
return packageNamePrefix + className;
}
private static String getGeneratedClassName(Metamodel entity) {
@ -166,20 +206,14 @@ public final class ClassWriter {
return entity.isJakartaDataStyle() ? '_' + className : className + '_';
}
private static String getGeneratedSuperclassName(Metamodel entity, String superClassName) {
if ( entity.isJakartaDataStyle() ) {
int lastDot = superClassName.lastIndexOf('.');
if ( lastDot<0 ) {
return '_' + superClassName;
}
else {
return superClassName.substring(0,lastDot+1)
+ '_' + superClassName.substring(lastDot+1);
}
}
else {
return superClassName + '_';
}
private static String getGeneratedSuperclassName(Element superClassElement, boolean jakartaDataStyle) {
final TypeElement typeElement = (TypeElement) superClassElement;
final String simpleName = typeElement.getSimpleName().toString();
final Element enclosingElement = typeElement.getEnclosingElement();
return (enclosingElement instanceof TypeElement
? getGeneratedSuperclassName( enclosingElement, jakartaDataStyle )
: ((PackageElement) enclosingElement).getQualifiedName().toString())
+ "." + (jakartaDataStyle ? '_' + simpleName : simpleName + '_');
}
private static String writeGeneratedAnnotation(Metamodel entity, Context context) {
@ -227,6 +261,7 @@ public final class ClassWriter {
final String annotation = entity.isJakartaDataStyle()
? "jakarta.data.metamodel.StaticMetamodel"
: "jakarta.persistence.metamodel.StaticMetamodel";
return "@" + entity.importType( annotation ) + "(" + entity.getSimpleName() + ".class)";
final String simpleName = entity.importType( entity.getQualifiedName() );
return "@" + entity.importType( annotation ) + "(" + simpleName + ".class)";
}
}

View File

@ -304,6 +304,15 @@ public final class Context {
return dataMetaEmbeddables.values();
}
public @Nullable Metamodel getMetamodel(String qualifiedName) {
if ( metaEntities.containsKey( qualifiedName ) ) {
return metaEntities.get( qualifiedName );
}
else {
return metaEmbeddables.get( qualifiedName );
}
}
public @Nullable Metamodel getMetaAuxiliary(String qualifiedName) {
return metaAuxiliaries.get( qualifiedName );
}

View File

@ -7,6 +7,7 @@ package org.hibernate.processor;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.processor.annotation.AnnotationMetaEntity;
import org.hibernate.processor.annotation.AnnotationMetaPackage;
import org.hibernate.processor.annotation.NonManagedMetamodel;
import org.hibernate.processor.model.Metamodel;
import org.hibernate.processor.util.Constants;
import org.hibernate.processor.xml.JpaDescriptorParser;
@ -22,6 +23,7 @@ import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
@ -82,6 +84,7 @@ import static org.hibernate.processor.util.TypeUtils.getAnnotationMirror;
import static org.hibernate.processor.util.TypeUtils.getAnnotationValue;
import static org.hibernate.processor.util.TypeUtils.hasAnnotation;
import static org.hibernate.processor.util.TypeUtils.isClassOrRecordType;
import static org.hibernate.processor.util.TypeUtils.isMemberType;
/**
* Main annotation processor.
@ -358,15 +361,22 @@ public class HibernateProcessor extends AbstractProcessor {
}
for ( Element element : roundEnvironment.getRootElements() ) {
processElement( element, null );
}
}
private void processElement(Element element, @Nullable Element parent) {
try {
if ( !included( element )
|| hasAnnotation( element, Constants.EXCLUDE )
|| hasPackageAnnotation( element, Constants.EXCLUDE ) ) {
|| hasPackageAnnotation( element, Constants.EXCLUDE )
|| element.getModifiers().contains( Modifier.PRIVATE ) ) {
// skip it completely
return;
}
else if ( isEntityOrEmbeddable( element ) ) {
else if ( isEntityOrEmbeddable( element ) && !element.getModifiers().contains( Modifier.PRIVATE )) {
context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated entity class '" + element + "'" );
handleRootElementAnnotationMirrors( element );
handleRootElementAnnotationMirrors( element, parent );
}
else if ( hasAuxiliaryAnnotations( element ) ) {
context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class '" + element + "'" );
@ -398,6 +408,42 @@ public class HibernateProcessor extends AbstractProcessor {
break;
}
}
if ( enclosesEntityOrEmbeddable( element ) ) {
AnnotationMetaEntity parentMeta = null;
if ( parent instanceof TypeElement parentElement ) {
final String key = parentElement.getQualifiedName().toString();
if ( context.getMetamodel( key ) instanceof AnnotationMetaEntity parentMetaEntity ) {
parentMeta = parentMetaEntity;
}
}
final NonManagedMetamodel metaEntity =
NonManagedMetamodel .create(
typeElement, context,
false, parentMeta );
context.addMetaEntity( metaEntity.getQualifiedName(), metaEntity );
if ( context.generateJakartaDataStaticMetamodel()) {
AnnotationMetaEntity parentDataMeta = null;
if ( parent instanceof TypeElement parentElement ) {
final String key = parentElement.getQualifiedName().toString();
if ( context.getDataMetaEntity( key ) instanceof AnnotationMetaEntity parentMetaEntity ) {
parentDataMeta = parentMetaEntity;
}
}
final NonManagedMetamodel dataMetaEntity =
NonManagedMetamodel .create(
typeElement, context,
true, parentDataMeta );
context.addDataMetaEntity( dataMetaEntity.getQualifiedName(), dataMetaEntity );
}
}
}
}
if ( isClassOrRecordType( element ) ) {
for ( final Element child : element.getEnclosedElements() ) {
if ( isClassOrRecordType( child ) ) {
processElement( child, element );
}
}
}
}
@ -411,7 +457,6 @@ public class HibernateProcessor extends AbstractProcessor {
}
}
}
}
private boolean hasPackageAnnotation(Element element, String annotation) {
final PackageElement pack = context.getElementUtils().getPackageOf( element ); // null for module descriptor
@ -430,7 +475,7 @@ public class HibernateProcessor extends AbstractProcessor {
}
for ( Metamodel entity : context.getMetaEntities() ) {
if ( !context.isAlreadyGenerated(entity) ) {
if ( !context.isAlreadyGenerated( entity ) && !isMemberType( entity.getElement() ) ) {
context.logMessage( Diagnostic.Kind.OTHER,
"Writing Jakarta Persistence metamodel for entity '" + entity + "'" );
ClassWriter.writeFile( entity, context );
@ -439,7 +484,7 @@ public class HibernateProcessor extends AbstractProcessor {
}
for ( Metamodel entity : context.getDataMetaEntities() ) {
if ( !context.isAlreadyGenerated(entity) ) {
if ( !context.isAlreadyGenerated( entity ) && !isMemberType( entity.getElement() ) ) {
context.logMessage( Diagnostic.Kind.OTHER,
"Writing Jakarta Data metamodel for entity '" + entity + "'" );
ClassWriter.writeFile( entity, context );
@ -516,6 +561,18 @@ public class HibernateProcessor extends AbstractProcessor {
return false;
}
private static boolean enclosesEntityOrEmbeddable(Element element) {
if ( !(element instanceof TypeElement typeElement) ) {
return false;
}
for ( final Element enclosedElement : typeElement.getEnclosedElements() ) {
if ( isEntityOrEmbeddable( enclosedElement ) || enclosesEntityOrEmbeddable( enclosedElement ) ) {
return true;
}
}
return false;
}
private static boolean isEntityOrEmbeddable(Element element) {
return hasAnnotation(
element,
@ -547,7 +604,7 @@ public class HibernateProcessor extends AbstractProcessor {
);
}
private void handleRootElementAnnotationMirrors(final Element element) {
private void handleRootElementAnnotationMirrors(final Element element, @Nullable Element parent) {
if ( isClassOrRecordType( element ) ) {
if ( isEntityOrEmbeddable( element ) ) {
final TypeElement typeElement = (TypeElement) element;
@ -564,12 +621,20 @@ public class HibernateProcessor extends AbstractProcessor {
+ "' since XML configuration is metadata complete.");
}
else {
AnnotationMetaEntity parentMetaEntity = null;
if ( parent instanceof TypeElement parentTypeElement ) {
if ( context.getMetamodel(
parentTypeElement.getQualifiedName().toString() )
instanceof AnnotationMetaEntity pme ) {
parentMetaEntity = pme;
}
}
final boolean requiresLazyMemberInitialization
= hasAnnotation( element, EMBEDDABLE, MAPPED_SUPERCLASS );
final AnnotationMetaEntity metaEntity =
AnnotationMetaEntity.create( typeElement, context,
requiresLazyMemberInitialization,
true, false );
true, false, parentMetaEntity );
if ( alreadyExistingMetaEntity != null ) {
metaEntity.mergeInMembers( alreadyExistingMetaEntity );
}
@ -583,10 +648,17 @@ public class HibernateProcessor extends AbstractProcessor {
// let a handwritten metamodel "override" the generated one
// (this is used in the Jakarta Data TCK)
&& !hasHandwrittenMetamodel(element) ) {
AnnotationMetaEntity parentDataEntity = null;
if ( parent instanceof TypeElement parentTypeElement ) {
if ( context.getDataMetaEntity( parentTypeElement.getQualifiedName().toString() )
instanceof AnnotationMetaEntity pme ) {
parentDataEntity = pme;
}
}
final AnnotationMetaEntity dataMetaEntity =
AnnotationMetaEntity.create( typeElement, context,
requiresLazyMemberInitialization,
true, true );
true, true, parentDataEntity );
// final Metamodel alreadyExistingDataMetaEntity =
// tryGettingExistingDataEntityFromContext( mirror, '_' + qualifiedName );
// if ( alreadyExistingDataMetaEntity != null ) {

View File

@ -80,11 +80,12 @@ import static org.hibernate.processor.annotation.QueryMethod.isOrderParam;
import static org.hibernate.processor.annotation.QueryMethod.isPageParam;
import static org.hibernate.processor.util.Constants.*;
import static org.hibernate.processor.util.NullnessUtil.castNonNull;
import static org.hibernate.processor.util.StringUtil.removeDollar;
import static org.hibernate.processor.util.TypeUtils.containsAnnotation;
import static org.hibernate.processor.util.TypeUtils.determineAccessTypeForHierarchy;
import static org.hibernate.processor.util.TypeUtils.determineAnnotationSpecifiedAccessType;
import static org.hibernate.processor.util.TypeUtils.extendsClass;
import static org.hibernate.processor.util.TypeUtils.findMappedSuperClass;
import static org.hibernate.processor.util.TypeUtils.findMappedSuperElement;
import static org.hibernate.processor.util.TypeUtils.getAnnotationMirror;
import static org.hibernate.processor.util.TypeUtils.getAnnotationValue;
import static org.hibernate.processor.util.TypeUtils.hasAnnotation;
@ -110,6 +111,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private final ImportContext importContext;
private final TypeElement element;
private final Map<String, MetaAttribute> members;
private TypeElement parentElement;
private final Context context;
private final boolean managed;
private boolean jakartaDataRepository;
@ -163,26 +165,32 @@ public class AnnotationMetaEntity extends AnnotationMeta {
public AnnotationMetaEntity(
TypeElement element, Context context, boolean managed,
boolean jakartaDataStaticMetamodel) {
boolean jakartaDataStaticMetamodel,
@Nullable AnnotationMeta parent) {
this.element = element;
this.context = context;
this.managed = managed;
this.members = new HashMap<>();
this.quarkusInjection = context.isQuarkusInjection();
this.importContext = new ImportContextImpl( getPackageName( context, element ) );
this.importContext = parent != null ? parent : new ImportContextImpl( getPackageName( context, element ) );
jakartaDataStaticModel = jakartaDataStaticMetamodel;
}
public static AnnotationMetaEntity create(TypeElement element, Context context) {
return create( element,context, false, false, false );
return create( element,context, false, false, false, null );
}
public static AnnotationMetaEntity create(
TypeElement element, Context context,
boolean lazilyInitialised, boolean managed,
boolean jakartaData) {
boolean jakartaData,
@Nullable AnnotationMetaEntity parent) {
final AnnotationMetaEntity annotationMetaEntity =
new AnnotationMetaEntity( element, context, managed, jakartaData );
new AnnotationMetaEntity( element, context, managed, jakartaData, parent );
if ( parent != null ) {
annotationMetaEntity.setParentElement( parent.element );
parent.addInnerClass( annotationMetaEntity );
}
if ( !lazilyInitialised ) {
annotationMetaEntity.init();
}
@ -225,18 +233,6 @@ public class AnnotationMetaEntity extends AnnotationMeta {
return getSimpleName() + '_';
}
/**
* If this is an "intermediate" class providing {@code @Query}
* annotations for the query by magical method name crap, then
* by convention it will be named with a trailing $ sign. Strip
* that off, so we get the standard constructor.
*/
private static String removeDollar(String simpleName) {
return simpleName.endsWith("$")
? simpleName.substring(0, simpleName.length()-1)
: simpleName;
}
@Override
public final String getQualifiedName() {
if ( qualifiedName == null ) {
@ -246,8 +242,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
}
@Override
public @Nullable String getSupertypeName() {
return repository ? null : findMappedSuperClass( this, context );
public @Nullable Element getSuperTypeElement() {
return repository ? null : findMappedSuperElement( this, context );
}
@Override
@ -270,6 +266,14 @@ public class AnnotationMetaEntity extends AnnotationMeta {
return new ArrayList<>( members.values() );
}
public void addInnerClass(AnnotationMetaEntity metaEntity) {
putMember( "INNER_" + metaEntity.getQualifiedName(), new InnerClassMetaAttribute( metaEntity ) );
}
public void setParentElement(TypeElement parentElement) {
this.parentElement = parentElement;
}
@Override
public boolean isMetaComplete() {
return false;
@ -363,7 +367,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
.toString();
}
protected final void init() {
protected void init() {
getContext().logMessage( Diagnostic.Kind.OTHER, "Initializing type '" + getQualifiedName() + "'" );
setupSession();

View File

@ -11,6 +11,7 @@ import org.hibernate.processor.model.ImportContext;
import org.hibernate.processor.model.MetaAttribute;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.tools.Diagnostic;
import java.util.ArrayList;
@ -69,7 +70,7 @@ public class AnnotationMetaPackage extends AnnotationMeta {
}
@Override
public @Nullable String getSupertypeName() {
public @Nullable Element getSuperTypeElement() {
return null;
}

View File

@ -66,7 +66,7 @@ public class DefaultConstructor implements MetaAttribute {
final StringBuilder declaration = new StringBuilder();
declaration
.append('\n');
if ( annotationMetaEntity.getSupertypeName() == null ) {
if ( annotationMetaEntity.getSuperTypeElement() == null ) {
declaration
.append("@")
.append(annotationMetaEntity.importType("jakarta.persistence.PersistenceUnit"));

View File

@ -0,0 +1,73 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.processor.annotation;
import org.hibernate.processor.model.MetaAttribute;
import org.hibernate.processor.model.Metamodel;
public class InnerClassMetaAttribute implements MetaAttribute {
private final AnnotationMeta metaEntity;
public InnerClassMetaAttribute(AnnotationMeta parent) {
this.metaEntity = parent;
}
public AnnotationMeta getMetaEntity() {
return metaEntity;
}
@Override
public boolean hasTypedAttribute() {
return true;
}
@Override
public boolean hasStringAttribute() {
return false;
}
@Override
public String getAttributeDeclarationString() {
// final StringBuilder decl = new StringBuilder()
// .append("\n/**\n * Static ID class for {@link ")
// .append( parent.getQualifiedName() )
// .append( "}\n **/\n" )
// .append( "public record Id" );
// String delimiter = "(";
// for ( MetaAttribute component : components ) {
// decl.append( delimiter ).append( parent.importType( component.getTypeDeclaration() ) )
// .append( ' ' ).append( component.getPropertyName() );
// delimiter = ", ";
// }
// return decl.append( ") {}" ).toString();
return "";
}
@Override
public String getAttributeNameDeclarationString() {
return "";
}
@Override
public String getMetaType() {
return "";
}
@Override
public String getPropertyName() {
return "";
}
@Override
public String getTypeDeclaration() {
return "";
}
@Override
public Metamodel getHostingEntity() {
return metaEntity;
}
}

View File

@ -0,0 +1,39 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.processor.annotation;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.processor.Context;
import javax.lang.model.element.TypeElement;
public class NonManagedMetamodel extends AnnotationMetaEntity {
public NonManagedMetamodel(TypeElement element, Context context, boolean jakartaDataStaticMetamodel, @Nullable AnnotationMeta parent) {
super( element, context, false, jakartaDataStaticMetamodel, parent );
}
public static NonManagedMetamodel create(
TypeElement element, Context context,
boolean jakartaDataStaticMetamodel,
@Nullable AnnotationMetaEntity parent) {
final NonManagedMetamodel metamodel =
new NonManagedMetamodel( element, context, jakartaDataStaticMetamodel, parent );
if ( parent != null ) {
metamodel.setParentElement( parent.getElement() );
parent.addInnerClass( metamodel );
}
return metamodel;
}
protected void init() {
// Initialization is not needed when non-managed class
}
@Override
public String javadoc() {
return "";
}
}

View File

@ -67,7 +67,7 @@ public class RepositoryConstructor implements MetaAttribute {
final StringBuilder declaration = new StringBuilder();
declaration
.append('\n');
if ( annotationMetaEntity.getSupertypeName() == null ) {
if ( annotationMetaEntity.getSuperTypeElement() == null ) {
declaration
.append("protected ");
if ( !dataRepository ) {
@ -96,7 +96,7 @@ public class RepositoryConstructor implements MetaAttribute {
.append(" ")
.append(sessionVariableName)
.append(") {\n");
if ( annotationMetaEntity.getSupertypeName() != null ) {
if ( annotationMetaEntity.getSuperTypeElement() != null ) {
declaration
.append("\tsuper(")
.append(sessionVariableName)
@ -112,7 +112,7 @@ public class RepositoryConstructor implements MetaAttribute {
}
declaration
.append("}");
if ( annotationMetaEntity.getSupertypeName() == null ) {
if ( annotationMetaEntity.getSuperTypeElement() == null ) {
declaration
.append("\n\n");
if (addOverrideAnnotation) {

View File

@ -19,7 +19,7 @@ public interface Metamodel extends ImportContext {
String getQualifiedName();
@Nullable String getSupertypeName();
@Nullable Element getSuperTypeElement();
String getPackageName();

View File

@ -98,6 +98,9 @@ public final class StringUtil {
}
public static String getUpperUnderscoreCaseFromLowerCamelCase(String lowerCamelCaseString) {
if ( lowerCamelCaseString.length() == 1 && isUpperCase( lowerCamelCaseString.charAt( 0 ) ) ) {
return "_" + lowerCamelCaseString;
}
final StringBuilder result = new StringBuilder();
int position = 0;
while ( position < lowerCamelCaseString.length() ) {
@ -116,4 +119,16 @@ public final class StringUtil {
&& isUpperCase( string.charAt( 0 ) )
&& isUpperCase( string.charAt( 1 ) );
}
/**
* If this is an "intermediate" class providing {@code @Query}
* annotations for the query by magical method name crap, then
* by convention it will be named with a trailing $ sign. Strip
* that off, so we get the standard constructor.
*/
public static String removeDollar(String simpleName) {
return simpleName.endsWith("$")
? simpleName.substring(0, simpleName.length()-1)
: simpleName;
}
}

View File

@ -598,7 +598,7 @@ public final class TypeUtils {
}
}
public static @Nullable String findMappedSuperClass(Metamodel entity, Context context) {
public static @Nullable Element findMappedSuperElement(Metamodel entity, Context context) {
final Element element = entity.getElement();
if ( element instanceof TypeElement typeElement ) {
TypeMirror superClass = typeElement.getSuperclass();
@ -607,7 +607,7 @@ public final class TypeUtils {
final DeclaredType declaredType = (DeclaredType) superClass;
final TypeElement superClassElement = (TypeElement) declaredType.asElement();
if ( extendsSuperMetaModel( superClassElement, entity.isMetaComplete(), context ) ) {
return superClassElement.getQualifiedName().toString();
return superClassElement;
}
superClass = superClassElement.getSuperclass();
}
@ -667,6 +667,10 @@ public final class TypeUtils {
return false;
}
public static boolean isMemberType(Element element) {
return element.getEnclosingElement() instanceof TypeElement;
}
static class EmbeddedAttributeVisitor extends SimpleTypeVisitor8<@Nullable TypeElement, Element> {
private final Context context;

View File

@ -113,6 +113,8 @@ import org.hibernate.type.MapType;
import org.hibernate.type.SetType;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaType;
import org.hibernate.type.descriptor.java.EnumJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType;
@ -120,6 +122,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.ObjectJdbcType;
import org.hibernate.type.spi.TypeConfiguration;
import javax.lang.model.element.TypeElement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -280,7 +283,7 @@ public abstract class MockSessionFactory
abstract boolean isEntityDefined(String entityName);
abstract String findEntityName(String typeName);
abstract TypeElement findEntityClass(String entityName);
abstract String qualifyName(String entityName);
@ -681,6 +684,39 @@ public abstract class MockSessionFactory
return SqlTypes.ARRAY;
}
private static class MockJavaType<X> implements BasicJavaType<X> {
private final String typeName;
public MockJavaType(String typeName) {
this.typeName = typeName;
}
@Override
public <X1> X1 unwrap(X value, Class<X1> type, WrapperOptions options) {
return null;
}
@Override
public <X1> X wrap(X1 value, WrapperOptions options) {
return null;
}
@Override
public String getTypeName() {
return typeName;
}
@Override
public Class<X> getJavaTypeClass() {
try {
return (Class<X>) Class.forName( typeName );
}
catch (ClassNotFoundException e) {
return null;
}
}
}
private class MockMappingMetamodelImpl extends MappingMetamodelImpl {
public MockMappingMetamodelImpl() {
super(typeConfiguration, serviceRegistry);
@ -774,21 +810,21 @@ public abstract class MockSessionFactory
@Override
public EntityDomainType<?> entity(String entityName) {
return isEntityDefined( entityName )
? new MockEntityDomainType<>( entityName )
: null;
}
@Override
public @Nullable EntityDomainType<?> findEntityType(@Nullable String entityName) {
if ( isEntityDefined(entityName) ) {
return new MockEntityDomainType<>(entityName);
final TypeElement entityClass = findEntityClass( entityName );
final String entityTypeName = entityClass == null ? entityName : entityClass.getQualifiedName().toString();
return new MockEntityDomainType<>(entityName, new MockJavaType<>( entityTypeName ));
}
else {
return null;
}
}
@Override
public @Nullable EntityDomainType<?> findEntityType(@Nullable String entityName) {
return entity( entityName );
}
@Override
public String qualifyImportableName(String queryName) {
if (isClassDefined(queryName)) {
@ -825,9 +861,12 @@ public abstract class MockSessionFactory
@Override
public <X> EntityDomainType<X> findEntityType(Class<X> cls) {
return isEntityDefined( cls.getName() )
? new MockEntityDomainType<X>( cls.getName() )
: null;
if ( isEntityDefined( cls.getName() ) ) {
return new MockEntityDomainType<>( cls.getName(), new MockJavaType<X>( cls.getName() ));
}
else {
return null;
}
}
@Override
@ -906,8 +945,8 @@ public abstract class MockSessionFactory
class MockEntityDomainType<X> extends EntityTypeImpl<X> {
public MockEntityDomainType(String entityName) {
super(entityName, entityName, false, true, false, null, null,
public MockEntityDomainType(String entityName, JavaType<X> javaType) {
super(entityName, entityName, false, true, false, javaType, null,
metamodel.getJpaMetamodel());
}
@ -991,7 +1030,7 @@ public abstract class MockSessionFactory
if (!entry.getValue().getEntityName().equals(getHibernateEntityName())
&& isSubtype(entry.getValue().getEntityName(), getHibernateEntityName())) {
final PersistentAttribute<? super Object, ?> subattribute
= new MockEntityDomainType<>(entry.getValue().getEntityName()).findAttribute(name);
= new MockEntityDomainType<>(entry.getValue().getEntityName(), new MockJavaType<>(entry.getValue().getEntityName()) ).findAttribute(name);
if (subattribute != null) {
return (SqmPathSource<?>) subattribute;
}
@ -1037,7 +1076,7 @@ public abstract class MockSessionFactory
owner,
name,
AttributeClassification.MANY_TO_ONE,
new MockEntityDomainType<>(type.getName()),
new MockEntityDomainType<>(type.getName(), new MockJavaType<>(type.getName())),
null,
null,
false,
@ -1095,7 +1134,9 @@ public abstract class MockSessionFactory
private DomainType<?> getDomainType(String entityName, CollectionType collectionType, ManagedDomainType<?> owner, Type elementType) {
if ( elementType.isEntityType() ) {
final String associatedEntityName = collectionType.getAssociatedEntityName(MockSessionFactory.this);
return new MockEntityDomainType<>(associatedEntityName);
final TypeElement associatedEntityEntityClass = findEntityClass( associatedEntityName );
final String associatedEntityTypeName = associatedEntityEntityClass == null ? associatedEntityName : associatedEntityEntityClass.getQualifiedName().toString();
return new MockEntityDomainType<>(associatedEntityName, new MockJavaType<>(associatedEntityTypeName));
}
else if ( elementType.isComponentType() ) {
final CompositeType compositeType = (CompositeType) elementType;

View File

@ -477,7 +477,8 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
&& findPropertyByPath(entityClass, fieldName, getDefaultAccessType(entityClass)) != null;
}
private TypeElement findEntityClass(String entityName) {
@Override
public TypeElement findEntityClass(String entityName) {
if (entityName == null) {
return null;
}

View File

@ -53,7 +53,7 @@ import static org.hibernate.processor.util.StringUtil.determineFullyQualifiedCla
import static org.hibernate.processor.util.StringUtil.isFullyQualified;
import static org.hibernate.processor.util.StringUtil.packageNameFromFullyQualifiedName;
import static org.hibernate.processor.util.TypeUtils.extractClosestRealTypeAsString;
import static org.hibernate.processor.util.TypeUtils.findMappedSuperClass;
import static org.hibernate.processor.util.TypeUtils.findMappedSuperElement;
import static org.hibernate.processor.util.TypeUtils.getElementKindForAccessType;
/**
@ -165,8 +165,8 @@ public class XmlMetaEntity implements Metamodel {
}
@Override
public @Nullable String getSupertypeName() {
return findMappedSuperClass( this, context );
public @Nullable Element getSuperTypeElement() {
return findMappedSuperElement( this, context );
}
public List<MetaAttribute> getMembers() {