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; package org.hibernate.processor;
import org.hibernate.processor.annotation.InnerClassMetaAttribute;
import org.hibernate.processor.model.MetaAttribute; import org.hibernate.processor.model.MetaAttribute;
import org.hibernate.processor.model.Metamodel; import org.hibernate.processor.model.Metamodel;
import org.hibernate.processor.util.StringUtil;
import javax.annotation.processing.FilerException; import javax.annotation.processing.FilerException;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind; 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.lang.model.element.TypeElement;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
import javax.tools.FileObject; import javax.tools.FileObject;
@ -18,7 +23,12 @@ import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List; 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. * 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(); String body = generateBody( entity, context ).toString();
FileObject fo = context.getProcessingEnvironment().getFiler().createSourceFile( FileObject fo = context.getProcessingEnvironment().getFiler().createSourceFile(
getFullyQualifiedClassName( entity, metaModelPackage ), getFullyQualifiedClassName( entity ),
entity.getElement() entity.getElement()
); );
OutputStream os = fo.openOutputStream(); OutputStream os = fo.openOutputStream();
@ -101,6 +111,15 @@ public final class ClassWriter {
pw.println(); pw.println();
final List<MetaAttribute> members = entity.getMembers(); 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 ) { for ( MetaAttribute metaMember : members ) {
if ( metaMember.hasStringAttribute() ) { if ( metaMember.hasStringAttribute() ) {
metaMember.getAttributeNameDeclarationString().lines() metaMember.getAttributeNameDeclarationString().lines()
@ -133,16 +152,29 @@ public final class ClassWriter {
} }
private static void printClassDeclaration(Metamodel entity, PrintWriter pw) { 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 " ); pw.print( "public " );
}
else if ( modifiers.contains( Modifier.PROTECTED ) ) {
pw.print( "protected " );
}
pw.print( "static " );
}
else {
pw.print( "public " );
}
if ( !entity.isImplementation() && !entity.isJakartaDataStyle() ) { if ( !entity.isImplementation() && !entity.isJakartaDataStyle() ) {
pw.print( "abstract " ); pw.print( "abstract " );
} }
pw.print( entity.isJakartaDataStyle() ? "interface " : "class " ); pw.print( entity.isJakartaDataStyle() ? "interface " : "class " );
pw.print( getGeneratedClassName(entity) ); pw.print( getGeneratedClassName(entity) );
String superClassName = entity.getSupertypeName(); final Element superTypeElement = entity.getSuperTypeElement();
if ( superClassName != null ) { if ( superTypeElement != null ) {
pw.print( " extends " + getGeneratedSuperclassName(entity, superClassName) ); pw.print( " extends " +
entity.importType(getGeneratedSuperclassName( superTypeElement, entity.isJakartaDataStyle() )) );
} }
if ( entity.isImplementation() ) { if ( entity.isImplementation() ) {
pw.print( entity.getElement().getKind() == ElementKind.CLASS ? " extends " : " implements " ); pw.print( entity.getElement().getKind() == ElementKind.CLASS ? " extends " : " implements " );
@ -152,13 +184,21 @@ public final class ClassWriter {
pw.println( " {" ); pw.println( " {" );
} }
private static String getFullyQualifiedClassName(Metamodel entity, String metaModelPackage) { private static String getFullyQualifiedClassName(Metamodel entity) {
String fullyQualifiedClassName = ""; final String metaModelPackage = entity.getPackageName();
if ( !metaModelPackage.isEmpty() ) { final String packageNamePrefix = !metaModelPackage.isEmpty() ? metaModelPackage + "." : "";
fullyQualifiedClassName = fullyQualifiedClassName + metaModelPackage + "."; final String className;
if ( entity.getElement().getKind() == ElementKind.PACKAGE ) {
className = getGeneratedClassName( entity );
} }
fullyQualifiedClassName = fullyQualifiedClassName + getGeneratedClassName( entity ); else {
return fullyQualifiedClassName; 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) { private static String getGeneratedClassName(Metamodel entity) {
@ -166,20 +206,14 @@ public final class ClassWriter {
return entity.isJakartaDataStyle() ? '_' + className : className + '_'; return entity.isJakartaDataStyle() ? '_' + className : className + '_';
} }
private static String getGeneratedSuperclassName(Metamodel entity, String superClassName) { private static String getGeneratedSuperclassName(Element superClassElement, boolean jakartaDataStyle) {
if ( entity.isJakartaDataStyle() ) { final TypeElement typeElement = (TypeElement) superClassElement;
int lastDot = superClassName.lastIndexOf('.'); final String simpleName = typeElement.getSimpleName().toString();
if ( lastDot<0 ) { final Element enclosingElement = typeElement.getEnclosingElement();
return '_' + superClassName; return (enclosingElement instanceof TypeElement
} ? getGeneratedSuperclassName( enclosingElement, jakartaDataStyle )
else { : ((PackageElement) enclosingElement).getQualifiedName().toString())
return superClassName.substring(0,lastDot+1) + "." + (jakartaDataStyle ? '_' + simpleName : simpleName + '_');
+ '_' + superClassName.substring(lastDot+1);
}
}
else {
return superClassName + '_';
}
} }
private static String writeGeneratedAnnotation(Metamodel entity, Context context) { private static String writeGeneratedAnnotation(Metamodel entity, Context context) {
@ -227,6 +261,7 @@ public final class ClassWriter {
final String annotation = entity.isJakartaDataStyle() final String annotation = entity.isJakartaDataStyle()
? "jakarta.data.metamodel.StaticMetamodel" ? "jakarta.data.metamodel.StaticMetamodel"
: "jakarta.persistence.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(); 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) { public @Nullable Metamodel getMetaAuxiliary(String qualifiedName) {
return metaAuxiliaries.get( qualifiedName ); return metaAuxiliaries.get( qualifiedName );
} }

View File

@ -7,6 +7,7 @@ package org.hibernate.processor;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.processor.annotation.AnnotationMetaEntity; import org.hibernate.processor.annotation.AnnotationMetaEntity;
import org.hibernate.processor.annotation.AnnotationMetaPackage; import org.hibernate.processor.annotation.AnnotationMetaPackage;
import org.hibernate.processor.annotation.NonManagedMetamodel;
import org.hibernate.processor.model.Metamodel; import org.hibernate.processor.model.Metamodel;
import org.hibernate.processor.util.Constants; import org.hibernate.processor.util.Constants;
import org.hibernate.processor.xml.JpaDescriptorParser; 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.Element;
import javax.lang.model.element.ElementKind; import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement; import javax.lang.model.element.PackageElement;
import javax.lang.model.element.QualifiedNameable; import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement; 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.getAnnotationValue;
import static org.hibernate.processor.util.TypeUtils.hasAnnotation; import static org.hibernate.processor.util.TypeUtils.hasAnnotation;
import static org.hibernate.processor.util.TypeUtils.isClassOrRecordType; import static org.hibernate.processor.util.TypeUtils.isClassOrRecordType;
import static org.hibernate.processor.util.TypeUtils.isMemberType;
/** /**
* Main annotation processor. * Main annotation processor.
@ -358,15 +361,22 @@ public class HibernateProcessor extends AbstractProcessor {
} }
for ( Element element : roundEnvironment.getRootElements() ) { for ( Element element : roundEnvironment.getRootElements() ) {
processElement( element, null );
}
}
private void processElement(Element element, @Nullable Element parent) {
try { try {
if ( !included( element ) if ( !included( element )
|| hasAnnotation( element, Constants.EXCLUDE ) || hasAnnotation( element, Constants.EXCLUDE )
|| hasPackageAnnotation( element, Constants.EXCLUDE ) ) { || hasPackageAnnotation( element, Constants.EXCLUDE )
|| element.getModifiers().contains( Modifier.PRIVATE ) ) {
// skip it completely // 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 + "'" ); context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated entity class '" + element + "'" );
handleRootElementAnnotationMirrors( element ); handleRootElementAnnotationMirrors( element, parent );
} }
else if ( hasAuxiliaryAnnotations( element ) ) { else if ( hasAuxiliaryAnnotations( element ) ) {
context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class '" + element + "'" ); context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class '" + element + "'" );
@ -398,6 +408,42 @@ public class HibernateProcessor extends AbstractProcessor {
break; 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) { private boolean hasPackageAnnotation(Element element, String annotation) {
final PackageElement pack = context.getElementUtils().getPackageOf( element ); // null for module descriptor 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() ) { for ( Metamodel entity : context.getMetaEntities() ) {
if ( !context.isAlreadyGenerated(entity) ) { if ( !context.isAlreadyGenerated( entity ) && !isMemberType( entity.getElement() ) ) {
context.logMessage( Diagnostic.Kind.OTHER, context.logMessage( Diagnostic.Kind.OTHER,
"Writing Jakarta Persistence metamodel for entity '" + entity + "'" ); "Writing Jakarta Persistence metamodel for entity '" + entity + "'" );
ClassWriter.writeFile( entity, context ); ClassWriter.writeFile( entity, context );
@ -439,7 +484,7 @@ public class HibernateProcessor extends AbstractProcessor {
} }
for ( Metamodel entity : context.getDataMetaEntities() ) { for ( Metamodel entity : context.getDataMetaEntities() ) {
if ( !context.isAlreadyGenerated(entity) ) { if ( !context.isAlreadyGenerated( entity ) && !isMemberType( entity.getElement() ) ) {
context.logMessage( Diagnostic.Kind.OTHER, context.logMessage( Diagnostic.Kind.OTHER,
"Writing Jakarta Data metamodel for entity '" + entity + "'" ); "Writing Jakarta Data metamodel for entity '" + entity + "'" );
ClassWriter.writeFile( entity, context ); ClassWriter.writeFile( entity, context );
@ -516,6 +561,18 @@ public class HibernateProcessor extends AbstractProcessor {
return false; 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) { private static boolean isEntityOrEmbeddable(Element element) {
return hasAnnotation( return hasAnnotation(
element, 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 ( isClassOrRecordType( element ) ) {
if ( isEntityOrEmbeddable( element ) ) { if ( isEntityOrEmbeddable( element ) ) {
final TypeElement typeElement = (TypeElement) element; final TypeElement typeElement = (TypeElement) element;
@ -564,12 +621,20 @@ public class HibernateProcessor extends AbstractProcessor {
+ "' since XML configuration is metadata complete."); + "' since XML configuration is metadata complete.");
} }
else { else {
AnnotationMetaEntity parentMetaEntity = null;
if ( parent instanceof TypeElement parentTypeElement ) {
if ( context.getMetamodel(
parentTypeElement.getQualifiedName().toString() )
instanceof AnnotationMetaEntity pme ) {
parentMetaEntity = pme;
}
}
final boolean requiresLazyMemberInitialization final boolean requiresLazyMemberInitialization
= hasAnnotation( element, EMBEDDABLE, MAPPED_SUPERCLASS ); = hasAnnotation( element, EMBEDDABLE, MAPPED_SUPERCLASS );
final AnnotationMetaEntity metaEntity = final AnnotationMetaEntity metaEntity =
AnnotationMetaEntity.create( typeElement, context, AnnotationMetaEntity.create( typeElement, context,
requiresLazyMemberInitialization, requiresLazyMemberInitialization,
true, false ); true, false, parentMetaEntity );
if ( alreadyExistingMetaEntity != null ) { if ( alreadyExistingMetaEntity != null ) {
metaEntity.mergeInMembers( alreadyExistingMetaEntity ); metaEntity.mergeInMembers( alreadyExistingMetaEntity );
} }
@ -583,10 +648,17 @@ public class HibernateProcessor extends AbstractProcessor {
// let a handwritten metamodel "override" the generated one // let a handwritten metamodel "override" the generated one
// (this is used in the Jakarta Data TCK) // (this is used in the Jakarta Data TCK)
&& !hasHandwrittenMetamodel(element) ) { && !hasHandwrittenMetamodel(element) ) {
AnnotationMetaEntity parentDataEntity = null;
if ( parent instanceof TypeElement parentTypeElement ) {
if ( context.getDataMetaEntity( parentTypeElement.getQualifiedName().toString() )
instanceof AnnotationMetaEntity pme ) {
parentDataEntity = pme;
}
}
final AnnotationMetaEntity dataMetaEntity = final AnnotationMetaEntity dataMetaEntity =
AnnotationMetaEntity.create( typeElement, context, AnnotationMetaEntity.create( typeElement, context,
requiresLazyMemberInitialization, requiresLazyMemberInitialization,
true, true ); true, true, parentDataEntity );
// final Metamodel alreadyExistingDataMetaEntity = // final Metamodel alreadyExistingDataMetaEntity =
// tryGettingExistingDataEntityFromContext( mirror, '_' + qualifiedName ); // tryGettingExistingDataEntityFromContext( mirror, '_' + qualifiedName );
// if ( alreadyExistingDataMetaEntity != null ) { // 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.annotation.QueryMethod.isPageParam;
import static org.hibernate.processor.util.Constants.*; import static org.hibernate.processor.util.Constants.*;
import static org.hibernate.processor.util.NullnessUtil.castNonNull; 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.containsAnnotation;
import static org.hibernate.processor.util.TypeUtils.determineAccessTypeForHierarchy; import static org.hibernate.processor.util.TypeUtils.determineAccessTypeForHierarchy;
import static org.hibernate.processor.util.TypeUtils.determineAnnotationSpecifiedAccessType; import static org.hibernate.processor.util.TypeUtils.determineAnnotationSpecifiedAccessType;
import static org.hibernate.processor.util.TypeUtils.extendsClass; 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.getAnnotationMirror;
import static org.hibernate.processor.util.TypeUtils.getAnnotationValue; import static org.hibernate.processor.util.TypeUtils.getAnnotationValue;
import static org.hibernate.processor.util.TypeUtils.hasAnnotation; import static org.hibernate.processor.util.TypeUtils.hasAnnotation;
@ -110,6 +111,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private final ImportContext importContext; private final ImportContext importContext;
private final TypeElement element; private final TypeElement element;
private final Map<String, MetaAttribute> members; private final Map<String, MetaAttribute> members;
private TypeElement parentElement;
private final Context context; private final Context context;
private final boolean managed; private final boolean managed;
private boolean jakartaDataRepository; private boolean jakartaDataRepository;
@ -163,26 +165,32 @@ public class AnnotationMetaEntity extends AnnotationMeta {
public AnnotationMetaEntity( public AnnotationMetaEntity(
TypeElement element, Context context, boolean managed, TypeElement element, Context context, boolean managed,
boolean jakartaDataStaticMetamodel) { boolean jakartaDataStaticMetamodel,
@Nullable AnnotationMeta parent) {
this.element = element; this.element = element;
this.context = context; this.context = context;
this.managed = managed; this.managed = managed;
this.members = new HashMap<>(); this.members = new HashMap<>();
this.quarkusInjection = context.isQuarkusInjection(); this.quarkusInjection = context.isQuarkusInjection();
this.importContext = new ImportContextImpl( getPackageName( context, element ) ); this.importContext = parent != null ? parent : new ImportContextImpl( getPackageName( context, element ) );
jakartaDataStaticModel = jakartaDataStaticMetamodel; jakartaDataStaticModel = jakartaDataStaticMetamodel;
} }
public static AnnotationMetaEntity create(TypeElement element, Context context) { 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( public static AnnotationMetaEntity create(
TypeElement element, Context context, TypeElement element, Context context,
boolean lazilyInitialised, boolean managed, boolean lazilyInitialised, boolean managed,
boolean jakartaData) { boolean jakartaData,
@Nullable AnnotationMetaEntity parent) {
final AnnotationMetaEntity annotationMetaEntity = 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 ) { if ( !lazilyInitialised ) {
annotationMetaEntity.init(); annotationMetaEntity.init();
} }
@ -225,18 +233,6 @@ public class AnnotationMetaEntity extends AnnotationMeta {
return getSimpleName() + '_'; 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 @Override
public final String getQualifiedName() { public final String getQualifiedName() {
if ( qualifiedName == null ) { if ( qualifiedName == null ) {
@ -246,8 +242,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
} }
@Override @Override
public @Nullable String getSupertypeName() { public @Nullable Element getSuperTypeElement() {
return repository ? null : findMappedSuperClass( this, context ); return repository ? null : findMappedSuperElement( this, context );
} }
@Override @Override
@ -270,6 +266,14 @@ public class AnnotationMetaEntity extends AnnotationMeta {
return new ArrayList<>( members.values() ); 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 @Override
public boolean isMetaComplete() { public boolean isMetaComplete() {
return false; return false;
@ -363,7 +367,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
.toString(); .toString();
} }
protected final void init() { protected void init() {
getContext().logMessage( Diagnostic.Kind.OTHER, "Initializing type '" + getQualifiedName() + "'" ); getContext().logMessage( Diagnostic.Kind.OTHER, "Initializing type '" + getQualifiedName() + "'" );
setupSession(); setupSession();

View File

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

View File

@ -66,7 +66,7 @@ public class DefaultConstructor implements MetaAttribute {
final StringBuilder declaration = new StringBuilder(); final StringBuilder declaration = new StringBuilder();
declaration declaration
.append('\n'); .append('\n');
if ( annotationMetaEntity.getSupertypeName() == null ) { if ( annotationMetaEntity.getSuperTypeElement() == null ) {
declaration declaration
.append("@") .append("@")
.append(annotationMetaEntity.importType("jakarta.persistence.PersistenceUnit")); .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(); final StringBuilder declaration = new StringBuilder();
declaration declaration
.append('\n'); .append('\n');
if ( annotationMetaEntity.getSupertypeName() == null ) { if ( annotationMetaEntity.getSuperTypeElement() == null ) {
declaration declaration
.append("protected "); .append("protected ");
if ( !dataRepository ) { if ( !dataRepository ) {
@ -96,7 +96,7 @@ public class RepositoryConstructor implements MetaAttribute {
.append(" ") .append(" ")
.append(sessionVariableName) .append(sessionVariableName)
.append(") {\n"); .append(") {\n");
if ( annotationMetaEntity.getSupertypeName() != null ) { if ( annotationMetaEntity.getSuperTypeElement() != null ) {
declaration declaration
.append("\tsuper(") .append("\tsuper(")
.append(sessionVariableName) .append(sessionVariableName)
@ -112,7 +112,7 @@ public class RepositoryConstructor implements MetaAttribute {
} }
declaration declaration
.append("}"); .append("}");
if ( annotationMetaEntity.getSupertypeName() == null ) { if ( annotationMetaEntity.getSuperTypeElement() == null ) {
declaration declaration
.append("\n\n"); .append("\n\n");
if (addOverrideAnnotation) { if (addOverrideAnnotation) {

View File

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

View File

@ -98,6 +98,9 @@ public final class StringUtil {
} }
public static String getUpperUnderscoreCaseFromLowerCamelCase(String lowerCamelCaseString) { public static String getUpperUnderscoreCaseFromLowerCamelCase(String lowerCamelCaseString) {
if ( lowerCamelCaseString.length() == 1 && isUpperCase( lowerCamelCaseString.charAt( 0 ) ) ) {
return "_" + lowerCamelCaseString;
}
final StringBuilder result = new StringBuilder(); final StringBuilder result = new StringBuilder();
int position = 0; int position = 0;
while ( position < lowerCamelCaseString.length() ) { while ( position < lowerCamelCaseString.length() ) {
@ -116,4 +119,16 @@ public final class StringUtil {
&& isUpperCase( string.charAt( 0 ) ) && isUpperCase( string.charAt( 0 ) )
&& isUpperCase( string.charAt( 1 ) ); && 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(); final Element element = entity.getElement();
if ( element instanceof TypeElement typeElement ) { if ( element instanceof TypeElement typeElement ) {
TypeMirror superClass = typeElement.getSuperclass(); TypeMirror superClass = typeElement.getSuperclass();
@ -607,7 +607,7 @@ public final class TypeUtils {
final DeclaredType declaredType = (DeclaredType) superClass; final DeclaredType declaredType = (DeclaredType) superClass;
final TypeElement superClassElement = (TypeElement) declaredType.asElement(); final TypeElement superClassElement = (TypeElement) declaredType.asElement();
if ( extendsSuperMetaModel( superClassElement, entity.isMetaComplete(), context ) ) { if ( extendsSuperMetaModel( superClassElement, entity.isMetaComplete(), context ) ) {
return superClassElement.getQualifiedName().toString(); return superClassElement;
} }
superClass = superClassElement.getSuperclass(); superClass = superClassElement.getSuperclass();
} }
@ -667,6 +667,10 @@ public final class TypeUtils {
return false; return false;
} }
public static boolean isMemberType(Element element) {
return element.getEnclosingElement() instanceof TypeElement;
}
static class EmbeddedAttributeVisitor extends SimpleTypeVisitor8<@Nullable TypeElement, Element> { static class EmbeddedAttributeVisitor extends SimpleTypeVisitor8<@Nullable TypeElement, Element> {
private final Context context; private final Context context;

View File

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

View File

@ -477,7 +477,8 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
&& findPropertyByPath(entityClass, fieldName, getDefaultAccessType(entityClass)) != null; && findPropertyByPath(entityClass, fieldName, getDefaultAccessType(entityClass)) != null;
} }
private TypeElement findEntityClass(String entityName) { @Override
public TypeElement findEntityClass(String entityName) {
if (entityName == null) { if (entityName == null) {
return 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.isFullyQualified;
import static org.hibernate.processor.util.StringUtil.packageNameFromFullyQualifiedName; import static org.hibernate.processor.util.StringUtil.packageNameFromFullyQualifiedName;
import static org.hibernate.processor.util.TypeUtils.extractClosestRealTypeAsString; 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; import static org.hibernate.processor.util.TypeUtils.getElementKindForAccessType;
/** /**
@ -165,8 +165,8 @@ public class XmlMetaEntity implements Metamodel {
} }
@Override @Override
public @Nullable String getSupertypeName() { public @Nullable Element getSuperTypeElement() {
return findMappedSuperClass( this, context ); return findMappedSuperElement( this, context );
} }
public List<MetaAttribute> getMembers() { public List<MetaAttribute> getMembers() {