From 582d736062ef4762c84d8d693ede0c1543f01deb Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 24 Feb 2024 20:22:24 +0100 Subject: [PATCH] HHH-17772 rework processor bookkeeping --- .../org/hibernate/jpamodelgen/Context.java | 53 +++++- .../JPAMetaModelEntityProcessor.java | 155 +++++++++++------- .../annotation/AnnotationMetaEntity.java | 28 ++-- 3 files changed, 155 insertions(+), 81 deletions(-) diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Context.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Context.java index e4b3d0d653..4bece049ce 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Context.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Context.java @@ -50,6 +50,17 @@ public final class Context { */ private final Map metaEmbeddables = new HashMap<>(); + /** + * Used for keeping track of parsed entities and mapped super classes (XML + annotations). + */ + private final Map dataMetaEntities = new HashMap<>(); + + /** + * Used for keeping track of parsed embeddable entities. These entities have to be kept separate since + * they are lazily initialized. + */ + private final Map dataMetaEmbeddables = new HashMap<>(); + private final Map metaAuxiliaries = new HashMap<>(); private final Map accessTypeInformation = new HashMap<>(); @@ -78,7 +89,7 @@ public final class Context { private boolean generateJakartaDataStaticMetamodel; // keep track of all classes for which model have been generated - private final Collection generatedModelClasses = new HashSet<>(); + private final Set generatedModelClasses = new HashSet<>(); // keep track of which named queries have been checked private final Set checkedNamedQueries = new HashSet<>(); @@ -233,6 +244,38 @@ public Collection getMetaEmbeddables() { return metaEmbeddables.values(); } + public boolean containsDataMetaEntity(String qualifiedName) { + return dataMetaEntities.containsKey( qualifiedName ); + } + + public @Nullable Metamodel getDataMetaEntity(String qualifiedName) { + return dataMetaEntities.get( qualifiedName ); + } + + public Collection getDataMetaEntities() { + return dataMetaEntities.values(); + } + + public void addDataMetaEntity(String qualifiedName, Metamodel metaEntity) { + dataMetaEntities.put( qualifiedName, metaEntity ); + } + + public boolean containsDataMetaEmbeddable(String qualifiedName) { + return dataMetaEmbeddables.containsKey( qualifiedName ); + } + + public @Nullable Metamodel getDataMetaEmbeddable(String qualifiedName) { + return dataMetaEmbeddables.get( qualifiedName ); + } + + public void addDataMetaEmbeddable(String qualifiedName, Metamodel metaEntity) { + dataMetaEmbeddables.put( qualifiedName, metaEntity ); + } + + public Collection getDataMetaEmbeddables() { + return dataMetaEmbeddables.values(); + } + public @Nullable Metamodel getMetaAuxiliary(String qualifiedName) { return metaAuxiliaries.get( qualifiedName ); } @@ -258,12 +301,12 @@ public TypeElement getTypeElementForFullyQualifiedName(String qualifiedName) { return elementUtils.getTypeElement( qualifiedName ); } - void markGenerated(String name) { - generatedModelClasses.add( name ); + void markGenerated(Metamodel metamodel) { + generatedModelClasses.add( metamodel ); } - boolean isAlreadyGenerated(String name) { - return generatedModelClasses.contains( name ); + boolean isAlreadyGenerated(Metamodel metamodel) { + return generatedModelClasses.contains( metamodel ); } public Set getElementsToRedo() { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java index 472cfcae2d..f285b11f31 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/JPAMetaModelEntityProcessor.java @@ -6,11 +6,12 @@ */ package org.hibernate.jpamodelgen; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.hibernate.jpamodelgen.annotation.AnnotationMetaEntity; +import org.hibernate.jpamodelgen.annotation.AnnotationMetaPackage; +import org.hibernate.jpamodelgen.model.Metamodel; +import org.hibernate.jpamodelgen.xml.JpaDescriptorParser; + import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; @@ -24,20 +25,29 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; - -import org.hibernate.jpamodelgen.annotation.AnnotationMetaEntity; -import org.hibernate.jpamodelgen.annotation.AnnotationMetaPackage; -import org.hibernate.jpamodelgen.model.Metamodel; -import org.hibernate.jpamodelgen.xml.JpaDescriptorParser; - -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import static java.lang.Boolean.parseBoolean; import static javax.lang.model.util.ElementFilter.fieldsIn; import static javax.lang.model.util.ElementFilter.methodsIn; +import static org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.ADD_GENERATED_ANNOTATION; +import static org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.ADD_GENERATION_DATE; +import static org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.ADD_SUPPRESS_WARNINGS_ANNOTATION; +import static org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.DEBUG_OPTION; +import static org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.FULLY_ANNOTATION_CONFIGURED_OPTION; +import static org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.LAZY_XML_PARSING; +import static org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.ORM_XML_OPTION; +import static org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.PERSISTENCE_XML_OPTION; import static org.hibernate.jpamodelgen.util.Constants.*; -import static org.hibernate.jpamodelgen.util.TypeUtils.*; -import static org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.*; +import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation; +import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationMirror; +import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationValue; +import static org.hibernate.jpamodelgen.util.TypeUtils.hasAnnotation; +import static org.hibernate.jpamodelgen.util.TypeUtils.isClassOrRecordType; /** * Main annotation processor. @@ -281,51 +291,62 @@ else if ( element instanceof TypeElement ) { private void createMetaModelClasses() { for ( Metamodel aux : context.getMetaAuxiliaries() ) { - final String key = aux.getQualifiedName(); - if ( !context.isAlreadyGenerated(key) ) { - context.logMessage( Diagnostic.Kind.OTHER, "Writing metamodel for auxiliary '" + aux + "'" ); + if ( !context.isAlreadyGenerated(aux) ) { + context.logMessage( Diagnostic.Kind.OTHER, + "Writing metamodel for auxiliary '" + aux + "'" ); ClassWriter.writeFile( aux, context ); - context.markGenerated(key); + context.markGenerated(aux); } } for ( Metamodel entity : context.getMetaEntities() ) { - final String key = entity.isJakartaDataStyle() - ? '_' + entity.getQualifiedName() - : entity.getQualifiedName(); - if ( !context.isAlreadyGenerated(key) ) { - context.logMessage( Diagnostic.Kind.OTHER, "Writing metamodel for entity '" + entity + "'" ); + if ( !context.isAlreadyGenerated(entity) ) { + context.logMessage( Diagnostic.Kind.OTHER, + "Writing Jakarta Persistence metamodel for entity '" + entity + "'" ); ClassWriter.writeFile( entity, context ); - context.markGenerated(key); + context.markGenerated(entity); } } - // we cannot process the delayed entities in any order. There might be dependencies between them. - // we need to process the top level entities first - final Collection toProcessEntities = context.getMetaEmbeddables(); - while ( !toProcessEntities.isEmpty() ) { - final Set processedEntities = new HashSet<>(); - int toProcessCountBeforeLoop = toProcessEntities.size(); - for ( Metamodel entity : toProcessEntities ) { + for ( Metamodel entity : context.getDataMetaEntities() ) { + if ( !context.isAlreadyGenerated(entity) ) { + context.logMessage( Diagnostic.Kind.OTHER, + "Writing Jakarta Data metamodel for entity '" + entity + "'" ); + ClassWriter.writeFile( entity, context ); + context.markGenerated(entity); + } + } + + processEmbeddables( context.getMetaEmbeddables() ); + processEmbeddables( context.getDataMetaEmbeddables() ); + } + + /** + * We cannot process the delayed classes in any order. + * There might be dependencies between them. + * We need to process the toplevel classes first. + */ + private void processEmbeddables(Collection models) { + while ( !models.isEmpty() ) { + final Set processed = new HashSet<>(); + final int toProcessCountBeforeLoop = models.size(); + for ( Metamodel metamodel : models ) { // see METAGEN-36 - final String key = entity.isJakartaDataStyle() - ? '_' + entity.getQualifiedName() - : entity.getQualifiedName(); - if ( context.isAlreadyGenerated(key) ) { - processedEntities.add( entity ); + if ( context.isAlreadyGenerated(metamodel) ) { + processed.add( metamodel ); } - else if ( !modelGenerationNeedsToBeDeferred( toProcessEntities, entity ) ) { + else if ( !modelGenerationNeedsToBeDeferred(models, metamodel ) ) { context.logMessage( Diagnostic.Kind.OTHER, - "Writing meta model for embeddable/mapped superclass " + entity + "Writing metamodel for embeddable " + metamodel ); - ClassWriter.writeFile( entity, context ); - context.markGenerated(key); - processedEntities.add( entity ); + ClassWriter.writeFile( metamodel, context ); + context.markGenerated(metamodel); + processed.add( metamodel ); } } - toProcessEntities.removeAll( processedEntities ); - if ( toProcessEntities.size() >= toProcessCountBeforeLoop ) { + models.removeAll( processed ); + if ( models.size() >= toProcessCountBeforeLoop ) { context.logMessage( Diagnostic.Kind.ERROR, "Potential endless loop in generation of entities." @@ -398,11 +419,11 @@ private boolean hasAuxiliaryAnnotations(Element element) { private void handleRootElementAnnotationMirrors(final Element element) { if ( isClassOrRecordType( element ) ) { - for ( AnnotationMirror mirror : element.getAnnotationMirrors() ) { + if ( hasAnnotation( element, ENTITY, MAPPED_SUPERCLASS, EMBEDDABLE ) ) { final TypeElement typeElement = (TypeElement) element; final String qualifiedName = typeElement.getQualifiedName().toString(); final Metamodel alreadyExistingMetaEntity = - tryGettingExistingEntityFromContext( mirror, qualifiedName ); + tryGettingExistingEntityFromContext( typeElement, qualifiedName ); if ( alreadyExistingMetaEntity != null && alreadyExistingMetaEntity.isMetaComplete() ) { context.logMessage( Diagnostic.Kind.OTHER, @@ -418,7 +439,7 @@ private void handleRootElementAnnotationMirrors(final Element element) { if ( alreadyExistingMetaEntity != null ) { metaEntity.mergeInMembers( alreadyExistingMetaEntity ); } - addMetaEntityToContext( mirror, metaEntity ); + addMetamodelToContext( typeElement, metaEntity ); if ( context.generateJakartaDataStaticMetamodel() // Don't generate a Jakarta Data metamodel // if this entity was partially mapped in XML @@ -427,11 +448,11 @@ private void handleRootElementAnnotationMirrors(final Element element) { AnnotationMetaEntity.create( typeElement, context, requiresLazyMemberInitialization, true, true ); // final Metamodel alreadyExistingDataMetaEntity = -// tryGettingExistingEntityFromContext( mirror, '_' + qualifiedName ); +// tryGettingExistingDataEntityFromContext( mirror, '_' + qualifiedName ); // if ( alreadyExistingDataMetaEntity != null ) { // dataMetaEntity.mergeInMembers( alreadyExistingDataMetaEntity ); // } - addMetaEntityToContext( mirror, dataMetaEntity ); + addDataMetamodelToContext( typeElement, dataMetaEntity ); } } } @@ -452,29 +473,39 @@ else if ( element instanceof PackageElement ) { //TODO: handle PackageElement } - private @Nullable Metamodel tryGettingExistingEntityFromContext(AnnotationMirror mirror, String qualifiedName) { - if ( isAnnotationMirrorOfType( mirror, ENTITY ) - || isAnnotationMirrorOfType( mirror, MAPPED_SUPERCLASS ) ) { + private @Nullable Metamodel tryGettingExistingEntityFromContext(TypeElement typeElement, String qualifiedName) { + if ( hasAnnotation( typeElement, ENTITY, MAPPED_SUPERCLASS ) ) { return context.getMetaEntity( qualifiedName ); } - else if ( isAnnotationMirrorOfType( mirror, EMBEDDABLE ) ) { + else if ( hasAnnotation( typeElement, EMBEDDABLE ) ) { return context.getMetaEmbeddable( qualifiedName ); } return null; } - private void addMetaEntityToContext(AnnotationMirror mirror, AnnotationMetaEntity metaEntity) { - final String key = metaEntity.isJakartaDataStyle() - ? '_' + metaEntity.getQualifiedName() - : metaEntity.getQualifiedName(); - if ( isAnnotationMirrorOfType( mirror, ENTITY ) ) { - context.addMetaEntity( key, metaEntity ); + private void addMetamodelToContext(TypeElement typeElement, AnnotationMetaEntity entity) { + final String key = entity.getQualifiedName(); + if ( hasAnnotation( typeElement, ENTITY ) ) { + context.addMetaEntity( key, entity ); } - else if ( isAnnotationMirrorOfType( mirror, MAPPED_SUPERCLASS ) ) { - context.addMetaEntity( key, metaEntity ); + else if ( hasAnnotation( typeElement, MAPPED_SUPERCLASS ) ) { + context.addMetaEntity( key, entity ); } - else if ( isAnnotationMirrorOfType( mirror, EMBEDDABLE ) ) { - context.addMetaEmbeddable( key, metaEntity ); + else if ( hasAnnotation( typeElement, EMBEDDABLE ) ) { + context.addMetaEmbeddable( key, entity ); + } + } + + private void addDataMetamodelToContext(TypeElement typeElement, AnnotationMetaEntity entity) { + final String key = entity.getQualifiedName(); + if ( hasAnnotation( typeElement, ENTITY ) ) { + context.addDataMetaEntity( key, entity ); + } + else if ( hasAnnotation( typeElement, MAPPED_SUPERCLASS ) ) { + context.addDataMetaEntity( key, entity ); + } + else if ( hasAnnotation( typeElement, EMBEDDABLE ) ) { + context.addDataMetaEmbeddable( key, entity ); } } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java index ad60f94c19..fce88fa9ee 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java @@ -113,7 +113,7 @@ public class AnnotationMetaEntity extends AnnotationMeta { private final Map members; private final Context context; private final boolean managed; - private boolean dataRepository; + private boolean jakartaDataRepository; private String qualifiedName; private final boolean jakartaDataStaticModel; @@ -306,7 +306,7 @@ public boolean isInjectable() { @Override public String scope() { - if (dataRepository) { + if (jakartaDataRepository) { return context.addTransactionScopedAnnotation() ? "javax.transaction.TransactionScoped" : "jakarta.enterprise.context.RequestScoped"; @@ -351,18 +351,18 @@ else if ( containsAnnotation( method, JD_INSERT, JD_UPDATE, JD_DELETE, JD_SAVE ) } } - dataRepository = hasAnnotation( element, JD_REPOSITORY ); + jakartaDataRepository = hasAnnotation( element, JD_REPOSITORY ); findSessionGetter( element ); - if ( !repository && dataRepository) { + if ( !repository && jakartaDataRepository) { repository = true; sessionType = HIB_STATELESS_SESSION; addDaoConstructor( null ); } - if ( dataRepository ) { + if ( jakartaDataRepository ) { addDefaultConstructor(); } - if ( managed ) { + if ( managed && !jakartaDataStaticModel ) { putMember( "class", new AnnotationMetaType(this) ); } @@ -452,7 +452,7 @@ private String addDaoConstructor(@Nullable ExecutableElement method) { context.addInjectAnnotation(), context.addNonnullAnnotation(), method != null, - dataRepository + jakartaDataRepository ) ); return sessionType; @@ -822,7 +822,7 @@ else if ( !context.getTypeUtils().isSameType( typeArgument, entity.asType() ) ) enabledFetchProfiles( method ), orderByList( method, entity ), context.addNonnullAnnotation(), - dataRepository + jakartaDataRepository ) ); } @@ -976,7 +976,7 @@ && matchesNaturalKey( method, entity ) ) { sessionType[1], enabledFetchProfiles( method ), context.addNonnullAnnotation(), - dataRepository + jakartaDataRepository ) ); } @@ -996,7 +996,7 @@ && matchesNaturalKey( method, entity ) ) { enabledFetchProfiles( method ), orderByList( method, entity ), context.addNonnullAnnotation(), - dataRepository + jakartaDataRepository ) ); } @@ -1029,7 +1029,7 @@ private void createSingleParameterFinder(ExecutableElement method, TypeMirror re sessionType[1], profiles, context.addNonnullAnnotation(), - dataRepository + jakartaDataRepository ) ); break; @@ -1047,7 +1047,7 @@ private void createSingleParameterFinder(ExecutableElement method, TypeMirror re sessionType[1], profiles, context.addNonnullAnnotation(), - dataRepository + jakartaDataRepository ) ); break; @@ -1067,7 +1067,7 @@ private void createSingleParameterFinder(ExecutableElement method, TypeMirror re profiles, orderByList( method, entity ), context.addNonnullAnnotation(), - dataRepository + jakartaDataRepository ) ); break; @@ -1302,7 +1302,7 @@ private void addQueryMethod( sessionType[0], sessionType[1], context.addNonnullAnnotation(), - dataRepository + jakartaDataRepository ); putMember( attribute.getPropertyName() + paramTypes, attribute );